diff --git a/build-logic/integration-testing/src/main/kotlin/gradlebuild.cross-version-tests.gradle.kts b/build-logic/integration-testing/src/main/kotlin/gradlebuild.cross-version-tests.gradle.kts index 7c87e794d52e..3e3db6c5f8b6 100644 --- a/build-logic/integration-testing/src/main/kotlin/gradlebuild.cross-version-tests.gradle.kts +++ b/build-logic/integration-testing/src/main/kotlin/gradlebuild.cross-version-tests.gradle.kts @@ -20,6 +20,7 @@ import gradlebuild.integrationtests.addDependenciesAndConfigurations import gradlebuild.integrationtests.addSourceSet import gradlebuild.integrationtests.configureIde import gradlebuild.integrationtests.createTestTask +import gradlebuild.integrationtests.setSystemPropertiesOfTestJVM plugins { java @@ -50,7 +51,7 @@ fun createQuickFeedbackTasks() { testType.executers.forEach { executer -> val taskName = "$executer${prefix.capitalize()}Test" val testTask = createTestTask(taskName, executer, sourceSet, testType) { - this.systemProperties["org.gradle.integtest.versions"] = "latest" + this.setSystemPropertiesOfTestJVM("latest") this.systemProperties["org.gradle.integtest.crossVersion"] = "true" // We should always be using JUnitPlatform at this point, so don't call useJUnitPlatform(), else this will 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 57f87d43db22..1e848d150bc8 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 @@ -17,6 +17,7 @@ import gradlebuild.basics.repoRoot import gradlebuild.cleanup.services.CachesCleaner import gradlebuild.integrationtests.tasks.DistributionTest +import gradlebuild.integrationtests.setSystemPropertiesOfTestJVM plugins { java @@ -38,7 +39,7 @@ tasks.withType().configureEach { shouldRunAfter("test") setJvmArgsOfTestJvm() - setSystemPropertiesOfTestJVM() + setSystemPropertiesOfTestJVM("default") configureGradleTestEnvironment() addSetUpAndTearDownActions() } @@ -93,13 +94,3 @@ fun DistributionTest.setJvmArgsOfTestJvm() { jvmArgs("-XX:MaxPermSize=768m") } } - -fun DistributionTest.setSystemPropertiesOfTestJVM() { - // use -PtestVersions=all or -PtestVersions=1.2,1.3… - val integTestVersionsSysProp = "org.gradle.integtest.versions" - if (project.hasProperty("testVersions")) { - systemProperties[integTestVersionsSysProp] = project.property("testVersions") - } else { - systemProperties[integTestVersionsSysProp] = "default" - } -} 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 9157430a99a0..97f17229a33f 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 @@ -24,6 +24,7 @@ import gradlebuild.basics.testSplitOnlyTestGradleVersion import gradlebuild.basics.testing.TestType import gradlebuild.capitalize import gradlebuild.integrationtests.extension.IntegrationTestExtension +import gradlebuild.integrationtests.tasks.DistributionTest import gradlebuild.integrationtests.tasks.IntegrationTest import gradlebuild.modules.extension.ExternalModulesExtension import gradlebuild.testing.services.BuildBucketProvider @@ -176,6 +177,17 @@ fun IntegrationTest.addDebugProperties() { } +fun DistributionTest.setSystemPropertiesOfTestJVM(defaultVersions: String) { + // use -PtestVersions=all or -PtestVersions=1.2,1.3… + val integTestVersionsSysProp = "org.gradle.integtest.versions" + if (project.hasProperty("testVersions")) { + systemProperties[integTestVersionsSysProp] = project.property("testVersions") + } else { + systemProperties[integTestVersionsSysProp] = defaultVersions + } +} + + internal fun Project.configureIde(testType: TestType) { val prefix = testType.prefix 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 1a72e44f6d58..31ef02901417 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("largeAndroidBuild2", RemoteProject) performanceTest.registerAndroidTestProject("santaTrackerAndroidBuild", RemoteProject) { remoteUri = 'https://github.com/gradle/santa-tracker-android.git' // Pinned from main branch - ref = '622fc64b7c39cb84e94174be3df9a54393348b45' + ref = '37a7d12c40f36657a0cb0181979c401c752bd328' doLast { addGoogleServicesJson(outputDirectory) } diff --git a/gradle.properties b/gradle.properties index a805dc586a37..07e6fa65f5a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ systemProp.gradle.internal.testdistribution.writeTraceFile=true systemProp.gradle.internal.testdistribution.queryResponseTimeout=PT20S # Default performance baseline -defaultPerformanceBaselines=8.0-commit-c6d0e67c +defaultPerformanceBaselines=8.0-commit-ca7079c8 # Disable Test Retry in GE plugin as long as Test Retry Gradle plugin is in use systemProp.gradle.enterprise.testretry.enabled=false diff --git a/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/BuildEventsIntegrationTest.groovy b/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/BuildEventsIntegrationTest.groovy index 15b0776a1d92..574a884e54c0 100644 --- a/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/BuildEventsIntegrationTest.groovy +++ b/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/BuildEventsIntegrationTest.groovy @@ -197,7 +197,7 @@ class BuildEventsIntegrationTest extends AbstractIntegrationSpec { run("thing") then: - output.count("EVENT:") == 14 + output.count("EVENT:") == 6 outputContains("EVENT: finish :buildSrc:processResources SKIPPED") outputContains("EVENT: finish :buildSrc:compileJava OK") outputContains("EVENT: finish :thing UP-TO-DATE") diff --git a/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/InternalBuildOperationEventsIntegrationTest.groovy b/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/InternalBuildOperationEventsIntegrationTest.groovy index ceabe73179b2..0d56dcbb179f 100644 --- a/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/InternalBuildOperationEventsIntegrationTest.groovy +++ b/subprojects/build-events/src/integTest/groovy/org/gradle/build/event/InternalBuildOperationEventsIntegrationTest.groovy @@ -95,7 +95,7 @@ class InternalBuildOperationEventsIntegrationTest extends AbstractIntegrationSpe run("a") then: - output.count("EVENT:") == 14 + output.count("EVENT:") == 6 outputContains("EVENT: task ':a'") when: 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 f313e2939846..f7ea3104b1c7 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 @@ -174,7 +174,7 @@ class BuildInitPluginIntegrationTest extends AbstractInitIntegrationSpec { when: executer.usingSettingsFile(customSettings) - executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 8.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: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout") runInitWith targetScriptDsl as BuildInitDsl then: diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIntegrationTest.groovy index a61bd9a030f4..0240eb76e53b 100644 --- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIntegrationTest.groovy +++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIntegrationTest.groovy @@ -47,8 +47,8 @@ class CompositeBuildBuildSrcIntegrationTest extends AbstractIntegrationSpec { run("help") then: - result.assertTaskExecuted(":buildSrc:assemble") - result.assertTaskExecuted(":child:buildSrc:assemble") + result.assertTaskExecuted(":buildSrc:jar") + result.assertTaskExecuted(":child:buildSrc:jar") outputContains("outer thing") outputContains("child thing") diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildCommandLineArgsIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildCommandLineArgsIntegrationTest.groovy index 22ea6c6721db..3458b1c740c7 100644 --- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildCommandLineArgsIntegrationTest.groovy +++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildCommandLineArgsIntegrationTest.groovy @@ -111,7 +111,7 @@ rootProject.buildFileName='build-copy.gradle' buildA.file("build-copy.gradle").copyFrom(buildA.buildFile) when: - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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") execute(buildA, ":checkDeps", ["--build-file", "build-copy.gradle"]) then: @@ -128,7 +128,7 @@ includeBuild '../buildB' """ when: - executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 8.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: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout") execute(buildA, ":checkDeps", ["--settings-file", "settings-copy.gradle"]) then: diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildTaskExcludeIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildTaskExcludeIntegrationTest.groovy index ab5017f73aed..d14a1fe23208 100644 --- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildTaskExcludeIntegrationTest.groovy +++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildTaskExcludeIntegrationTest.groovy @@ -228,8 +228,9 @@ class CompositeBuildTaskExcludeIntegrationTest extends AbstractCompositeBuildTas expect: succeeds("greeting", ":build-logic:classes") 2.times { - succeeds("greeting", "-x", ":build-logic:classes") - result.assertTaskNotExecuted(":build-logic:classes") + succeeds("greeting", "-x", ":build-logic:jar") + result.assertTaskNotExecuted(":build-logic:jar") + result.assertTaskNotExecuted(":build-logic:compileJava") } } diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/BuildStateFactory.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/BuildStateFactory.java index 37cc899d7e71..c69830c59bdc 100644 --- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/BuildStateFactory.java +++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/BuildStateFactory.java @@ -105,7 +105,7 @@ private StartParameterInternal buildSrcStartParameterFor(File buildSrcDir, Start buildSrcStartParameter.setCurrentDir(buildSrcDir); buildSrcStartParameter.setProjectProperties(containingBuildParameters.getProjectProperties()); buildSrcStartParameter.doNotSearchUpwards(); - buildSrcStartParameter.setProfile(containingBuildParameters.isProfile()); + buildSrcStartParameter.setInitScripts(containingBuildParameters.getInitScripts()); return buildSrcStartParameter; } } 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 0a40e61d5af3..2e502a0ac1c2 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 @@ -23,9 +23,7 @@ import org.gradle.api.internal.composite.CompositeBuildContext; import org.gradle.api.model.ObjectFactory; import org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor; -import org.gradle.internal.build.BuildStateRegistry; -import org.gradle.internal.build.IncludedBuildFactory; -import org.gradle.internal.event.ListenerManager; +import org.gradle.internal.buildtree.GlobalDependencySubstitutionRegistry; import org.gradle.internal.reflect.Instantiator; import org.gradle.internal.service.ServiceRegistration; import org.gradle.internal.service.scopes.AbstractPluginServiceRegistry; @@ -61,19 +59,16 @@ public void configure(ServiceRegistration serviceRegistration) { serviceRegistration.add(BuildStateFactory.class); serviceRegistration.add(DefaultIncludedBuildFactory.class); serviceRegistration.add(DefaultIncludedBuildTaskGraph.class); + serviceRegistration.add(DefaultIncludedBuildRegistry.class); } - public BuildStateRegistry createIncludedBuildRegistry(CompositeBuildContext context, - Instantiator instantiator, - ListenerManager listenerManager, - ObjectFactory objectFactory, - NotationParser moduleSelectorNotationParser, - ImmutableAttributesFactory attributesFactory, - BuildStateFactory buildStateFactory, - IncludedBuildFactory includedBuildFactory) { + public GlobalDependencySubstitutionRegistry createGlobalDependencySubstitutionRegistry(CompositeBuildContext context, + Instantiator instantiator, + ObjectFactory objectFactory, + NotationParser moduleSelectorNotationParser, + ImmutableAttributesFactory attributesFactory) { NotationParser capabilityNotationParser = new CapabilityNotationParserFactory(false).create(); - IncludedBuildDependencySubstitutionsBuilder dependencySubstitutionsBuilder = new IncludedBuildDependencySubstitutionsBuilder(context, instantiator, objectFactory, attributesFactory, moduleSelectorNotationParser, capabilityNotationParser); - return new DefaultIncludedBuildRegistry(includedBuildFactory, dependencySubstitutionsBuilder, listenerManager, buildStateFactory); + return new IncludedBuildDependencySubstitutionsBuilder(context, instantiator, objectFactory, attributesFactory, moduleSelectorNotationParser, capabilityNotationParser); } public CompositeBuildContext createCompositeBuildContext() { 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 b8b86b3ad7fe..b9256d4f1968 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 @@ -20,7 +20,6 @@ import org.gradle.api.GradleException; import org.gradle.api.artifacts.component.BuildIdentifier; import org.gradle.api.internal.BuildDefinition; -import org.gradle.api.internal.GradleInternal; import org.gradle.api.internal.SettingsInternal; import org.gradle.api.internal.artifacts.DefaultBuildIdentifier; import org.gradle.initialization.buildsrc.BuildSrcDetector; @@ -32,7 +31,6 @@ import org.gradle.internal.build.RootBuildState; import org.gradle.internal.build.StandAloneNestedBuild; import org.gradle.internal.buildtree.NestedBuildTree; -import org.gradle.internal.composite.IncludedBuildInternal; import org.gradle.internal.concurrent.CompositeStoppable; import org.gradle.internal.concurrent.Stoppable; import org.gradle.internal.event.ListenerManager; @@ -45,16 +43,13 @@ import java.util.Comparator; 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.Set; import java.util.function.Consumer; public class DefaultIncludedBuildRegistry implements BuildStateRegistry, Stoppable { private final IncludedBuildFactory includedBuildFactory; - private final IncludedBuildDependencySubstitutionsBuilder dependencySubstitutionsBuilder; private final BuildAddedListener buildAddedBroadcaster; private final BuildStateFactory buildStateFactory; @@ -65,12 +60,9 @@ public class DefaultIncludedBuildRegistry implements BuildStateRegistry, Stoppab private final Map includedBuildsByRootDir = new LinkedHashMap<>(); private final Map includedBuildDirectoriesByPath = new LinkedHashMap<>(); private final Deque pendingIncludedBuilds = new ArrayDeque<>(); - private boolean registerSubstitutionsForRootBuild; - private final Set currentlyConfiguring = new HashSet<>(); - public DefaultIncludedBuildRegistry(IncludedBuildFactory includedBuildFactory, IncludedBuildDependencySubstitutionsBuilder dependencySubstitutionsBuilder, ListenerManager listenerManager, BuildStateFactory buildStateFactory) { + public DefaultIncludedBuildRegistry(IncludedBuildFactory includedBuildFactory, ListenerManager listenerManager, BuildStateFactory buildStateFactory) { this.includedBuildFactory = includedBuildFactory; - this.dependencySubstitutionsBuilder = dependencySubstitutionsBuilder; this.buildAddedBroadcaster = listenerManager.getBroadcaster(BuildAddedListener.class); this.buildStateFactory = buildStateFactory; } @@ -149,13 +141,6 @@ public BuildState getBuild(BuildIdentifier buildIdentifier) { return buildState; } - @Override - public void afterConfigureRootBuild() { - if (registerSubstitutionsForRootBuild) { - dependencySubstitutionsBuilder.build(rootBuild); - } - } - @Override public void finalizeIncludedBuilds() { while (!pendingIncludedBuilds.isEmpty()) { @@ -164,13 +149,6 @@ public void finalizeIncludedBuilds() { } } - @Override - public void registerSubstitutionsFor(IncludedBuildState build) { - currentlyConfiguring.add(build); - dependencySubstitutionsBuilder.build(build); - currentlyConfiguring.remove(build); - } - @Override public StandAloneNestedBuild getBuildSrcNestedBuild(BuildState owner) { return buildSrcBuildsByOwner.get(owner); @@ -203,28 +181,6 @@ public NestedBuildTree addNestedBuildTree(BuildDefinition buildDefinition, Build return buildStateFactory.createNestedTree(buildDefinition, buildIdentifier, identityPath, owner); } - @Override - public void registerSubstitutionsForRootBuild() { - registerSubstitutionsForRootBuild = true; - } - - @Override - public void ensureConfigured(IncludedBuildState buildToConfigure) { - if (currentlyConfiguring.contains(buildToConfigure)) { - return; - } - currentlyConfiguring.add(buildToConfigure); - buildToConfigure.ensureProjectsConfigured(); - GradleInternal gradle = buildToConfigure.getMutableModel(); - for (IncludedBuildInternal reference : gradle.includedBuilds()) { - BuildState target = reference.getTarget(); - if (target instanceof IncludedBuildState) { - dependencySubstitutionsBuilder.build((IncludedBuildState) target); - } - } - currentlyConfiguring.remove(buildToConfigure); - } - @Override public void visitBuilds(Consumer visitor) { List ordered = new ArrayList<>(buildsByIdentifier.values()); 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 0147ad39fe3b..78730cf09f10 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 @@ -18,12 +18,12 @@ import org.gradle.api.artifacts.component.BuildIdentifier; import org.gradle.api.internal.BuildDefinition; +import org.gradle.initialization.IncludedBuildSpec; import org.gradle.initialization.exception.ExceptionAnalyser; import org.gradle.internal.build.AbstractBuildState; import org.gradle.internal.build.BuildState; import org.gradle.internal.build.ExecutionResult; import org.gradle.internal.build.StandAloneNestedBuild; -import org.gradle.internal.buildtree.BuildModelParameters; import org.gradle.internal.buildtree.BuildTreeFinishExecutor; import org.gradle.internal.buildtree.BuildTreeLifecycleController; import org.gradle.internal.buildtree.BuildTreeLifecycleControllerFactory; @@ -60,19 +60,11 @@ class DefaultNestedBuild extends AbstractBuildState implements StandAloneNestedB BuildScopeServices buildScopeServices = getBuildServices(); ExceptionAnalyser exceptionAnalyser = buildScopeServices.get(ExceptionAnalyser.class); - BuildModelParameters modelParameters = buildScopeServices.get(BuildModelParameters.class); BuildTreeWorkExecutor workExecutor = new DefaultBuildTreeWorkExecutor(); BuildTreeLifecycleControllerFactory buildTreeLifecycleControllerFactory = buildScopeServices.get(BuildTreeLifecycleControllerFactory.class); - // On completion of the action, finish only this build and do not finish any other builds - // When the build model is required, then do not finish anything on completion of the action - // The root build will take care of finishing this build later, if not finished now - BuildTreeFinishExecutor finishExecutor; - if (modelParameters.isRequiresBuildModel()) { - finishExecutor = new DoNothingBuildFinishExecutor(exceptionAnalyser); - } else { - finishExecutor = new FinishThisBuildOnlyFinishExecutor(exceptionAnalyser); - } + // On completion of the action, do not finish this build. The root build will take care of finishing this build later + BuildTreeFinishExecutor finishExecutor = new DoNothingBuildFinishExecutor(exceptionAnalyser); buildTreeLifecycleController = buildTreeLifecycleControllerFactory.createController(getBuildController(), workExecutor, finishExecutor); } @@ -96,6 +88,11 @@ public boolean isImplicitBuild() { return true; } + @Override + public BuildState getOwner() { + return owner; + } + @Override public ExecutionResult finishBuild() { return getBuildController().finishBuild(null); @@ -121,6 +118,10 @@ public File getBuildRootDir() { return buildDefinition.getBuildRootDir(); } + @Override + public void assertCanAdd(IncludedBuildSpec includedBuildSpec) { + } + private static class DoNothingBuildFinishExecutor implements BuildTreeFinishExecutor { private final ExceptionAnalyser exceptionAnalyser; diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/IncludedBuildDependencySubstitutionsBuilder.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/IncludedBuildDependencySubstitutionsBuilder.java index f36be881630d..26bd69a8ef5f 100644 --- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/IncludedBuildDependencySubstitutionsBuilder.java +++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/IncludedBuildDependencySubstitutionsBuilder.java @@ -25,17 +25,15 @@ import org.gradle.api.model.ObjectFactory; import org.gradle.internal.build.CompositeBuildParticipantBuildState; import org.gradle.internal.build.IncludedBuildState; +import org.gradle.internal.build.RootBuildState; +import org.gradle.internal.buildtree.GlobalDependencySubstitutionRegistry; import org.gradle.internal.reflect.Instantiator; import org.gradle.internal.typeconversion.NotationParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Set; -public class IncludedBuildDependencySubstitutionsBuilder { - private static final Logger LOGGER = LoggerFactory.getLogger(IncludedBuildDependencySubstitutionsBuilder.class); - +public class IncludedBuildDependencySubstitutionsBuilder implements GlobalDependencySubstitutionRegistry { private final CompositeBuildContext context; private final Instantiator instantiator; private final ObjectFactory objectFactory; @@ -44,12 +42,14 @@ public class IncludedBuildDependencySubstitutionsBuilder { private final NotationParser capabilitiesParser; private final Set processed = new HashSet<>(); - public IncludedBuildDependencySubstitutionsBuilder(CompositeBuildContext context, - Instantiator instantiator, - ObjectFactory objectFactory, - ImmutableAttributesFactory attributesFactory, - NotationParser moduleSelectorNotationParser, - NotationParser capabilitiesParser) { + public IncludedBuildDependencySubstitutionsBuilder( + CompositeBuildContext context, + Instantiator instantiator, + ObjectFactory objectFactory, + ImmutableAttributesFactory attributesFactory, + NotationParser moduleSelectorNotationParser, + NotationParser capabilitiesParser + ) { this.context = context; this.instantiator = instantiator; this.objectFactory = objectFactory; @@ -58,7 +58,18 @@ public IncludedBuildDependencySubstitutionsBuilder(CompositeBuildContext context this.capabilitiesParser = capabilitiesParser; } - public void build(IncludedBuildState build) { + @Override + public void registerSubstitutionsFor(CompositeBuildParticipantBuildState build) { + if (build instanceof IncludedBuildState) { + build((IncludedBuildState) build); + } else if (build instanceof RootBuildState) { + build((RootBuildState)build); + } else { + throw new IllegalArgumentException(); + } + } + + private void build(IncludedBuildState build) { if (processed.contains(build)) { // This may happen during early resolution, where we iterate through all builds to find only // the ones for which we need to register substitutions early so that they are available @@ -76,8 +87,8 @@ public void build(IncludedBuildState build) { } } - public void build(CompositeBuildParticipantBuildState rootBuildState) { - context.addAvailableModules(rootBuildState.getAvailableModules()); + private void build(RootBuildState build) { + context.addAvailableModules(build.getAvailableModules()); } private DependencySubstitutionsInternal resolveDependencySubstitutions(IncludedBuildState build) { diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/plugins/CompositeBuildPluginResolverContributor.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/plugins/CompositeBuildPluginResolverContributor.java index 333c245c861e..fc481c4c752e 100644 --- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/plugins/CompositeBuildPluginResolverContributor.java +++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/plugins/CompositeBuildPluginResolverContributor.java @@ -18,8 +18,6 @@ import org.gradle.api.internal.project.HoldsProjectState; import org.gradle.internal.build.BuildIncluder; -import org.gradle.internal.build.BuildState; -import org.gradle.internal.build.BuildStateRegistry; import org.gradle.internal.build.IncludedBuildState; import org.gradle.plugin.management.internal.InvalidPluginRequestException; import org.gradle.plugin.management.internal.PluginRequestInternal; @@ -40,8 +38,8 @@ public class CompositeBuildPluginResolverContributor implements PluginResolverCo private final CompositeBuildPluginResolver resolver; - public CompositeBuildPluginResolverContributor(BuildStateRegistry buildRegistry, BuildState consumingBuild, BuildIncluder buildIncluder) { - this.resolver = new CompositeBuildPluginResolver(buildRegistry, consumingBuild, buildIncluder); + public CompositeBuildPluginResolverContributor(BuildIncluder buildIncluder) { + this.resolver = new CompositeBuildPluginResolver(buildIncluder); } @Override @@ -54,9 +52,9 @@ public void collectResolversInto(Collection resolvers) { resolvers.add(resolver); } - private static class PluginResult { - static final PluginResult NOT_FOUND_IN_ANY_BUILD = new PluginResult(); - static final PluginResult NO_INCLUDED_BUILDS = new PluginResult(); + private abstract static class PluginResult { + static final PluginResult NOT_FOUND_IN_ANY_BUILD = new PluginResult() {}; + static final PluginResult NO_INCLUDED_BUILDS = new PluginResult() {}; } private static class ResolvedPlugin extends PluginResult { @@ -68,15 +66,11 @@ public ResolvedPlugin(PluginResolution resolution) { } private static class CompositeBuildPluginResolver implements PluginResolver { - private final BuildStateRegistry buildRegistry; - private final BuildState consumingBuild; private final BuildIncluder buildIncluder; private final Map results = new ConcurrentHashMap<>(); - private CompositeBuildPluginResolver(BuildStateRegistry buildRegistry, BuildState consumingBuild, BuildIncluder buildIncluder) { - this.buildRegistry = buildRegistry; - this.consumingBuild = consumingBuild; + private CompositeBuildPluginResolver(BuildIncluder buildIncluder) { this.buildIncluder = buildIncluder; } @@ -99,13 +93,11 @@ private PluginResult doResolve(PluginId pluginId) { } private PluginResult resolvePluginFromIncludedBuilds(PluginId requestedPluginId) { - if (buildRegistry.getIncludedBuilds().isEmpty()) { + Collection includedBuilds = buildIncluder.getIncludedBuildsForPluginResolution(); + if (includedBuilds.isEmpty()) { return PluginResult.NO_INCLUDED_BUILDS; } - for (IncludedBuildState build : buildRegistry.getIncludedBuilds()) { - if (build == consumingBuild || build.isImplicitBuild() || build.isPluginBuild()) { - continue; - } + for (IncludedBuildState build : includedBuilds) { Optional pluginResolution = build.withState(gradleInternal -> LocalPluginResolution.resolvePlugin(gradleInternal, requestedPluginId)); if (pluginResolution.isPresent()) { return new ResolvedPlugin(pluginResolution.get()); @@ -115,9 +107,8 @@ private PluginResult resolvePluginFromIncludedBuilds(PluginId requestedPluginId) } private PluginResolution resolveFromIncludedPluginBuilds(PluginId requestedPluginId) { - for (IncludedBuildState build : buildIncluder.includeRegisteredPluginBuilds()) { - buildRegistry.ensureConfigured(build); - + for (IncludedBuildState build : buildIncluder.getRegisteredPluginBuilds()) { + buildIncluder.prepareForPluginResolution(build); Optional pluginResolution = build.withState(gradleInternal -> LocalPluginResolution.resolvePlugin(gradleInternal, requestedPluginId)); if (pluginResolution.isPresent()) { return pluginResolution.get(); 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 50745d91bf08..92a5ad44a0f0 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 @@ -73,7 +73,6 @@ class DefaultIncludedBuildRegistryTest extends Specification { ) def registry = new DefaultIncludedBuildRegistry( includedBuildFactory, - Stub(IncludedBuildDependencySubstitutionsBuilder), listenerManager, factory ) 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 007e789bce70..d6e4ab2a4861 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 @@ -24,7 +24,6 @@ import org.gradle.initialization.exception.ExceptionAnalyser import org.gradle.internal.build.BuildLifecycleController import org.gradle.internal.build.BuildModelControllerServices import org.gradle.internal.build.BuildState -import org.gradle.internal.build.ExecutionResult import org.gradle.internal.buildtree.BuildModelParameters import org.gradle.internal.buildtree.BuildTreeFinishExecutor import org.gradle.internal.buildtree.BuildTreeLifecycleController @@ -75,7 +74,7 @@ class DefaultNestedBuildTest extends Specification { return new DefaultNestedBuild(buildIdentifier, Path.path(":a:b:c"), buildDefinition, owner, tree) } - def "runs action and finishes build when model is not required by root build"() { + def "runs action and does not finish build"() { given: services.add(new BuildModelParameters(false, false, false, false, false, false, false)) def build = build() @@ -94,29 +93,7 @@ class DefaultNestedBuildTest extends Specification { 1 * buildTreeController.scheduleAndRunTasks() >> { finishExecutor.finishBuildTree([]) } - 1 * controller.finishBuild(_) >> ExecutionResult.succeeded() - } - - def "runs action but does not finish build when model is required by root build"() { - given: - services.add(new BuildModelParameters(false, false, false, true, false, false, false)) - def build = build() - - when: - def result = build.run(action) - - then: - result == '' - - then: - 1 * action.apply(!null) >> { BuildTreeLifecycleController controller -> - controller.scheduleAndRunTasks() - '' - } - 1 * buildTreeController.scheduleAndRunTasks() >> { - finishExecutor.finishBuildTree([]) - } - 0 * controller.finishBuild(_, _) + 0 * controller.finishBuild(_) } def "can have null result"() { diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcChangesIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcChangesIntegrationTest.groovy index e1dace931cac..234887492c15 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcChangesIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcChangesIntegrationTest.groovy @@ -103,7 +103,7 @@ class ConfigurationCacheBuildSrcChangesIntegrationTest extends AbstractConfigura doLast { println("NOT CI") } } } - assemble { + jar { dependsOn("run") } } diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcIntegrationTest.groovy index 399dbdd52487..b31ca43b0174 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildSrcIntegrationTest.groovy @@ -62,14 +62,14 @@ class ConfigurationCacheBuildSrcIntegrationTest extends AbstractConfigurationCac configurationCacheRun("greeting") then: - result.assertTaskExecuted(":buildSrc:build") + result.assertTaskExecuted(":buildSrc:jar") result.assertTaskExecuted(":greeting") when: configurationCacheRun("greeting") then: - result.assertTasksExecuted(":greeting") + result.assertTasksExecuted(":greeting") // buildSrc tasks are not executed outputContains("yo configuration cache") configurationCache.assertStateLoaded() } diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheLambdaIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheLambdaIntegrationTest.groovy index d548e53d6cbd..1f81c2cd5bd4 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheLambdaIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheLambdaIntegrationTest.groovy @@ -18,7 +18,7 @@ package org.gradle.configurationcache class ConfigurationCacheLambdaIntegrationTest extends AbstractConfigurationCacheIntegrationTest { - def "restores task fields whose value is a serializable #kind Java lambda"() { + def "restores task fields whose value is a #kind Java lambda"() { given: file("buildSrc/src/main/java/my/LambdaTask.java").tap { parentFile.mkdirs() @@ -30,28 +30,41 @@ class ConfigurationCacheLambdaIntegrationTest extends AbstractConfigurationCache public class LambdaTask extends DefaultTask { + // Test with serializable lambdas that should work as-is, as well as non-serializable lambdas which should + // be forced to become serializable by the instrumentation: + public interface NonSerializableSupplier { + T get(); + } public interface SerializableSupplier extends java.io.Serializable { T get(); } - private SerializableSupplier supplier; + private SerializableSupplier serializableSupplier; + private NonSerializableSupplier nonSerializableSupplier; - public void setSupplier(SerializableSupplier supplier) { - this.supplier = supplier; + public void setSerializableSupplier(SerializableSupplier supplier) { + this.serializableSupplier = supplier; + } + + public void setNonSerializableSupplier(NonSerializableSupplier supplier) { + this.nonSerializableSupplier = supplier; } public void setNonInstanceCapturingLambda() { final int i = getName().length(); - setSupplier(() -> i); + setSerializableSupplier(() -> i); + setNonSerializableSupplier(() -> i); } public void setInstanceCapturingLambda() { - setSupplier(() -> getName().length()); + setSerializableSupplier(() -> getName().length()); + setNonSerializableSupplier(() -> getName().length()); } @TaskAction void printValue() { - System.out.println("this.supplier.get() -> " + this.supplier.get()); + System.out.println("this.serializableSupplier.get() -> " + this.serializableSupplier.get()); + System.out.println("this.nonSerializableSupplier.get() -> " + this.nonSerializableSupplier.get()); } } """ @@ -68,7 +81,7 @@ class ConfigurationCacheLambdaIntegrationTest extends AbstractConfigurationCache configurationCacheRun "ok" then: - outputContains("this.supplier.get() -> 2") + outputContains("this.serializableSupplier.get() -> 2\nthis.nonSerializableSupplier.get() -> 2") where: kind | expression @@ -194,4 +207,53 @@ class ConfigurationCacheLambdaIntegrationTest extends AbstractConfigurationCache then: outputContains("args: [-Dfoo=bar]") } + + def "lambda serialization can handle implementation methods with the same name"() { + given: + file("buildSrc/src/main/java/my/LambdaPlugin.java").tap { + parentFile.mkdirs() + text = """ + package my; + + import org.gradle.api.*; + + import javax.inject.Inject; + + class TaskA extends DefaultTask { + @Inject + public TaskA() { } + } + + class TaskB extends DefaultTask { + @Inject + public TaskB() { } + } + + public class LambdaPlugin implements Plugin { + // Use these overloads as lambda implementation methods - they should appear in SerializedLambda + static void foo(TaskA taskA) { } + static void foo(TaskB taskB) { } + + @Override + public void apply(Project project) { + Action actionA = LambdaPlugin::foo; + Action actionB = LambdaPlugin::foo; + + project.getTasks().register("a", TaskA.class, task -> task.doLast(a -> actionA.execute((TaskA) a))); + project.getTasks().register("b", TaskB.class, task -> task.doLast(b -> actionB.execute((TaskB) b))); + } + } + """ + } + buildFile << """ + apply plugin: my.LambdaPlugin + """ + + when: + configurationCacheRun("a", "b") + configurationCacheRun("a", "b") + + then: + succeeds("a", "b") + } } diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest.groovy index 9395374ee373..b9e2114d0159 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest.groovy @@ -98,7 +98,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a") - modelsReused(":", ":b", ":c", ":d") + modelsReused(":", ":b", ":c", ":d", ":buildSrc") } } @@ -173,7 +173,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":") projectConfigured(":b") // has not been consumed by project dependency previously, but is now modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } when: @@ -208,7 +208,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } } @@ -286,7 +286,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":") projectConfigured(":b") // has not been consumed by project dependency previously, but is now modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } when: @@ -321,7 +321,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } } @@ -398,7 +398,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } when: @@ -433,7 +433,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } } @@ -511,7 +511,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a", ":b", ":c") - modelsReused(":") + modelsReused(":", ":buildSrc") } when: @@ -546,7 +546,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a", ":b") - modelsReused(":", ":c") + modelsReused(":", ":c", ":buildSrc") } } @@ -637,7 +637,7 @@ class IsolatedProjectToolingModelsWithDependencyResolutionIntegrationTest extend projectConfigured(":") projectConfigured(":d") modelsCreated(":a", ":b", ":c") - modelsReused(":", ":d") + modelsReused(":", ":d", ":buildSrc") } } } diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiBuildActionIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiBuildActionIntegrationTest.groovy index d857c83be01f..3e9df3b23a98 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiBuildActionIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiBuildActionIntegrationTest.groovy @@ -88,7 +88,7 @@ class IsolatedProjectsToolingApiBuildActionIntegrationTest extends AbstractIsola fileChanged("build.gradle") projectConfigured(":buildSrc") modelsCreated(":") - modelsReused(":a", ":b") + modelsReused(":a", ":b", ":buildSrc") } outputContains("creating model for root project 'root'") @@ -123,7 +123,7 @@ class IsolatedProjectsToolingApiBuildActionIntegrationTest extends AbstractIsola projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a") - modelsReused(":", ":b") + modelsReused(":", ":b", ":buildSrc") } outputContains("creating model for project ':a'") } @@ -299,7 +299,7 @@ class IsolatedProjectsToolingApiBuildActionIntegrationTest extends AbstractIsola projectConfigured(":buildSrc") projectsConfigured(":") modelsCreated(":a") - modelsReused(":", ":b", ":c") + modelsReused(":", ":b", ":c", ":buildSrc") } when: @@ -329,7 +329,7 @@ class IsolatedProjectsToolingApiBuildActionIntegrationTest extends AbstractIsola projectConfigured(":buildSrc") projectsConfigured(":") modelsCreated(":b") - modelsReused(":", ":a", ":c") + modelsReused(":", ":a", ":c", ":buildSrc") } } @@ -397,7 +397,7 @@ class IsolatedProjectsToolingApiBuildActionIntegrationTest extends AbstractIsola fileChanged("build.gradle") projectConfigured(":buildSrc") modelsCreated(":") - modelsReused(":a", ":b") + modelsReused(":a", ":b", ":buildSrc") } outputContains("creating model for root project 'root'") @@ -472,7 +472,7 @@ class IsolatedProjectsToolingApiBuildActionIntegrationTest extends AbstractIsola fileChanged("build.gradle") projectConfigured(":buildSrc") modelsCreated(":") - modelsReused(":a", ":b") + modelsReused(":a", ":b", ":buildSrc") } outputContains("creating model for root project 'root'") diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiCoupledProjectsIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiCoupledProjectsIntegrationTest.groovy index 5e478491f759..7b375cf5d49e 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiCoupledProjectsIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiCoupledProjectsIntegrationTest.groovy @@ -99,7 +99,7 @@ class IsolatedProjectsToolingApiCoupledProjectsIntegrationTest extends AbstractI projectConfigured(":buildSrc") modelsCreated(":a", ":b") modelsQueriedAndNotPresent(":") - modelsReused(":c") + modelsReused(":c", ":buildSrc") problem("Build file 'build.gradle': Cannot access project ':a' from project ':'", 3) problem("Build file 'build.gradle': Cannot access project ':b' from project ':'") } @@ -166,7 +166,7 @@ class IsolatedProjectsToolingApiCoupledProjectsIntegrationTest extends AbstractI fileChanged("a/build.gradle") projectConfigured(":buildSrc") modelsQueriedAndNotPresent(":", ":a") - modelsReused(":b") + modelsReused(":b", ":buildSrc") problem("Build file 'a/build.gradle': Cannot access project ':' from project ':a'", 2) } } @@ -237,7 +237,7 @@ class IsolatedProjectsToolingApiCoupledProjectsIntegrationTest extends AbstractI projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a", ":b") - modelsReused(":", ":c") + modelsReused(":", ":c", ":buildSrc") problem("Build file 'b/build.gradle': Cannot access project ':a' from project ':b'") } @@ -270,7 +270,7 @@ class IsolatedProjectsToolingApiCoupledProjectsIntegrationTest extends AbstractI projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a", ":b") - modelsReused(":", ":c") + modelsReused(":", ":c", ":buildSrc") problem("Build file 'b/build.gradle': Cannot access project ':a' from project ':b'") } } @@ -341,7 +341,7 @@ class IsolatedProjectsToolingApiCoupledProjectsIntegrationTest extends AbstractI projectConfigured(":buildSrc") projectsConfigured(":", ":a") // :a and :b are coupled, so configure a but reuse its model modelsCreated(":b") - modelsReused(":", ":a", ":c") + modelsReused(":", ":a", ":c", ":buildSrc") problem("Build file 'b/build.gradle': Cannot access project ':a' from project ':b'") } } diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiIModelQueryIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiIModelQueryIntegrationTest.groovy index 18170cddb302..66a365a74b07 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiIModelQueryIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiIModelQueryIntegrationTest.groovy @@ -79,6 +79,7 @@ class IsolatedProjectsToolingApiIModelQueryIntegrationTest extends AbstractIsola fileChanged("build.gradle") projectConfigured(":buildSrc") modelsCreated(":") + modelsReused(":buildSrc") } outputContains("creating model for root project 'root'") diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiParallelConfigurationIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiParallelConfigurationIntegrationTest.groovy index c8b0c0912a28..8d975fa54f8f 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiParallelConfigurationIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiParallelConfigurationIntegrationTest.groovy @@ -109,7 +109,7 @@ class IsolatedProjectsToolingApiParallelConfigurationIntegrationTest extends Abs projectConfigured(":buildSrc") projectConfigured(":") modelsCreated(":a", ":b") - modelsReused(":") + modelsReused(":", ":buildSrc") } when: diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiPhasedBuildActionIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiPhasedBuildActionIntegrationTest.groovy index c5cb71abe501..84111b14a1dd 100644 --- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiPhasedBuildActionIntegrationTest.groovy +++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsToolingApiPhasedBuildActionIntegrationTest.groovy @@ -103,7 +103,7 @@ class IsolatedProjectsToolingApiPhasedBuildActionIntegrationTest extends Abstrac fileChanged("build.gradle") projectConfigured(":buildSrc") modelsCreated(":") - modelsReused(":a", ":b") + modelsReused(":a", ":b", ":buildSrc") } outputContains("creating model for root project 'root'") } diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/CachedBuildState.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/CachedBuildState.kt index ac9551e15ddc..5c410df3388f 100644 --- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/CachedBuildState.kt +++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/CachedBuildState.kt @@ -51,7 +51,15 @@ class ProjectWithNoWork( ) : CachedProjectState(path, projectDir, buildFile) -data class BuildToStore(val build: VintageGradleBuild, val hasWork: Boolean) +data class BuildToStore( + val build: VintageGradleBuild, + // Does this build have work scheduled? + val hasWork: Boolean, + // Does this build have a child build with work scheduled? + val hasChildren: Boolean +) { + fun hasChildren() = BuildToStore(build, hasWork, true) +} /** diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheBuild.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheBuild.kt index 1b8f6547cfb8..9660ef61ee2c 100644 --- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheBuild.kt +++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheBuild.kt @@ -16,10 +16,11 @@ package org.gradle.configurationcache +import org.gradle.api.artifacts.component.BuildIdentifier import org.gradle.api.internal.BuildDefinition import org.gradle.api.internal.GradleInternal import org.gradle.api.internal.project.ProjectInternal -import org.gradle.internal.build.CompositeBuildParticipantBuildState +import org.gradle.internal.build.BuildState import org.gradle.util.Path import java.io.File @@ -28,7 +29,7 @@ interface ConfigurationCacheBuild { val gradle: GradleInternal - val state: CompositeBuildParticipantBuildState + val state: BuildState fun registerRootProject(rootProjectName: String, projectDir: File, buildDir: File) @@ -40,4 +41,6 @@ interface ConfigurationCacheBuild { fun createProjects() fun addIncludedBuild(buildDefinition: BuildDefinition, settingsFile: File?): ConfigurationCacheBuild + + fun getBuildSrcOf(ownerId: BuildIdentifier): ConfigurationCacheBuild } diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheHost.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheHost.kt index c2a754a4a6b1..d53b7804b3f9 100644 --- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheHost.kt +++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheHost.kt @@ -16,6 +16,7 @@ package org.gradle.configurationcache +import org.gradle.api.artifacts.component.BuildIdentifier import org.gradle.api.internal.BuildDefinition import org.gradle.api.internal.GradleInternal import org.gradle.api.internal.SettingsInternal @@ -33,9 +34,7 @@ import org.gradle.initialization.layout.BuildLayout import org.gradle.internal.Factory import org.gradle.internal.build.BuildState import org.gradle.internal.build.BuildStateRegistry -import org.gradle.internal.build.CompositeBuildParticipantBuildState import org.gradle.internal.build.RootBuildState -import org.gradle.internal.build.StandAloneNestedBuild import org.gradle.internal.file.PathToFileResolver import org.gradle.internal.reflect.Instantiator import org.gradle.internal.resource.StringTextResource @@ -62,7 +61,7 @@ class ConfigurationCacheHost internal constructor( } override fun createBuild(settingsFile: File?): ConfigurationCacheBuild = - DefaultConfigurationCacheBuild(gradle, service(), service(), settingsFile) + DefaultConfigurationCacheBuild(gradle.owner, service(), service(), settingsFile) override fun service(serviceType: Class): T = gradle.services.get(serviceType) @@ -79,7 +78,7 @@ class ConfigurationCacheHost internal constructor( get() = state.mutableModel override val hasScheduledWork: Boolean - get() = if (state is StandAloneNestedBuild) false else gradle.taskGraph.size() > 0 + get() = gradle.taskGraph.size() > 0 override val scheduledWork: List get() { @@ -91,7 +90,7 @@ class ConfigurationCacheHost internal constructor( private inner class DefaultConfigurationCacheBuild( - override val gradle: GradleInternal, + override val state: BuildState, private val fileResolver: PathToFileResolver, private val buildStateRegistry: BuildStateRegistry, private val settingsFile: File? @@ -101,13 +100,15 @@ class ConfigurationCacheHost internal constructor( val buildDirs = mutableMapOf() init { - require(gradle.owner is CompositeBuildParticipantBuildState) gradle.run { attachSettings(createSettings()) setBaseProjectClassLoaderScope(coreScope) } } + override val gradle: GradleInternal + get() = state.mutableModel + override fun registerRootProject(rootProjectName: String, projectDir: File, buildDir: File) { // Root project is registered when the settings are created, just need to adjust its properties val descriptor = rootProjectDescriptor() @@ -166,7 +167,11 @@ class ConfigurationCacheHost internal constructor( state.projects.getProject(Path.path(path)).mutableModel override fun addIncludedBuild(buildDefinition: BuildDefinition, settingsFile: File?): ConfigurationCacheBuild { - return DefaultConfigurationCacheBuild(buildStateRegistry.addIncludedBuild(buildDefinition).mutableModel, fileResolver, buildStateRegistry, settingsFile) + return DefaultConfigurationCacheBuild(buildStateRegistry.addIncludedBuild(buildDefinition), fileResolver, buildStateRegistry, settingsFile) + } + + override fun getBuildSrcOf(ownerId: BuildIdentifier): ConfigurationCacheBuild { + return DefaultConfigurationCacheBuild(buildStateRegistry.getBuildSrcNestedBuild(buildStateRegistry.getBuild(ownerId))!!, fileResolver, buildStateRegistry, null) } private @@ -210,9 +215,6 @@ class ConfigurationCacheHost internal constructor( private val projectDescriptorRegistry get() = (gradle.settings as DefaultSettings).projectDescriptorRegistry - - override - val state: CompositeBuildParticipantBuildState = gradle.owner as CompositeBuildParticipantBuildState } private 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 5951f0c039e4..cc1bf8dcf7a4 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 @@ -17,14 +17,12 @@ package org.gradle.configurationcache import org.gradle.api.artifacts.component.BuildIdentifier -import org.gradle.api.cache.Cleanup import org.gradle.api.file.FileCollection import org.gradle.api.internal.BuildDefinition import org.gradle.api.internal.FeaturePreviews import org.gradle.api.internal.GradleInternal import org.gradle.api.internal.SettingsInternal.BUILD_SRC -import org.gradle.api.internal.cache.CacheResourceConfigurationInternal -import org.gradle.api.provider.Property +import org.gradle.api.internal.cache.CacheConfigurationsInternal.UnlockableProperty import org.gradle.api.provider.Provider import org.gradle.api.services.internal.BuildServiceProvider import org.gradle.api.services.internal.BuildServiceRegistryInternal @@ -63,8 +61,10 @@ import org.gradle.internal.build.BuildProjectRegistry import org.gradle.internal.build.BuildState import org.gradle.internal.build.BuildStateRegistry import org.gradle.internal.build.IncludedBuildState +import org.gradle.internal.build.NestedBuildState import org.gradle.internal.build.PublicBuildPath import org.gradle.internal.build.RootBuildState +import org.gradle.internal.build.StandAloneNestedBuild import org.gradle.internal.build.event.BuildEventListenerRegistryInternal import org.gradle.internal.buildoption.FeatureFlags import org.gradle.internal.buildtree.BuildTreeWorkGraph @@ -208,12 +208,16 @@ class ConfigurationCacheState( private suspend fun DefaultWriteContext.writeBuildsInTree(rootBuild: VintageGradleBuild, buildEventListeners: List>) { val requiredBuildServicesPerBuild = buildEventListeners.groupBy { it.buildIdentifier } - val builds = mutableListOf() + val builds = mutableMapOf() host.visitBuilds { build -> - val hasWork = build.hasScheduledWork || build.isRootBuild - builds.add(BuildToStore(build, hasWork)) + val state = build.state + builds[state] = BuildToStore(build, build.hasScheduledWork, build.isRootBuild) + if (build.hasScheduledWork && state is StandAloneNestedBuild) { + // Also require the owner of a buildSrc build + builds[state.owner] = builds.getValue(state.owner).hasChildren() + } } - writeCollection(builds) { build -> + writeCollection(builds.values) { build -> writeBuildState( build, StoredBuildTreeState( @@ -234,16 +238,20 @@ class ConfigurationCacheState( private suspend fun DefaultWriteContext.writeBuildState(build: BuildToStore, buildTreeState: StoredBuildTreeState, rootBuild: VintageGradleBuild) { val state = build.build.state - if (!build.hasWork) { + if (!build.hasWork && !build.hasChildren) { writeEnum(BuildType.BuildWithNoWork) writeBuildWithNoWork(state, rootBuild) } else if (state is RootBuildState) { writeEnum(BuildType.RootBuild) writeBuildContent(build.build, buildTreeState) - } else { - require(state is IncludedBuildState) + } else if (state is IncludedBuildState) { writeEnum(BuildType.IncludedBuild) writeIncludedBuild(state, buildTreeState) + } else if (state is StandAloneNestedBuild) { + writeEnum(BuildType.BuildSrcBuild) + writeBuildSrcBuild(state, buildTreeState) + } else { + throw IllegalArgumentException() } } @@ -254,6 +262,7 @@ class ConfigurationCacheState( BuildType.BuildWithNoWork -> readBuildWithNoWork(rootBuild) BuildType.RootBuild -> readBuildContent(rootBuild) BuildType.IncludedBuild -> readIncludedBuild(rootBuild) + BuildType.BuildSrcBuild -> readBuildSrcBuild(rootBuild) } } @@ -272,21 +281,42 @@ class ConfigurationCacheState( private suspend fun DefaultReadContext.readIncludedBuild(rootBuild: ConfigurationCacheBuild): CachedBuildState { - lateinit var definition: BuildDefinition val build = withGradleIsolate(rootBuild.gradle, userTypesCodec) { val settingsFile = read() as File? - definition = readIncludedBuildDefinition(rootBuild) + val definition = readIncludedBuildDefinition(rootBuild) rootBuild.addIncludedBuild(definition, settingsFile) } // Decode the build state using the contextualized IO service for the build - return build.gradle.serviceOf().readIncludedBuildStateFrom(stateFileFor(definition), build) + return build.gradle.serviceOf().readIncludedBuildStateFrom(stateFileFor((build.state as NestedBuildState).buildDefinition), build) + } + + private + suspend fun DefaultWriteContext.writeBuildSrcBuild(state: StandAloneNestedBuild, buildTreeState: StoredBuildTreeState) { + val gradle = state.mutableModel + withGradleIsolate(gradle, userTypesCodec) { + write(state.owner.buildIdentifier) + } + // Encode the build state using the contextualized IO service for the nested build + state.projects.withMutableStateOfAllProjects { + gradle.serviceOf().writeIncludedBuildStateTo(stateFileFor(state.buildDefinition), buildTreeState) + } + } + + private + suspend fun DefaultReadContext.readBuildSrcBuild(rootBuild: ConfigurationCacheBuild): CachedBuildState { + val build = withGradleIsolate(rootBuild.gradle, userTypesCodec) { + val ownerIdentifier = readNonNull() + rootBuild.getBuildSrcOf(ownerIdentifier) + } + // Decode the build state using the contextualized IO service for the build + return build.gradle.serviceOf().readIncludedBuildStateFrom(stateFileFor((build.state as NestedBuildState).buildDefinition), build) } private suspend fun DefaultWriteContext.writeBuildWithNoWork(state: BuildState, rootBuild: VintageGradleBuild) { withGradleIsolate(rootBuild.gradle, userTypesCodec) { writeString(state.identityPath.path) - if (state.isProjectsLoaded && state.projects.rootProject.isCreated) { + if (state.projectsAvailable) { writeBoolean(true) writeString(state.projects.rootProject.name) writeCollection(state.projects.allProjects) { project -> @@ -315,39 +345,47 @@ class ConfigurationCacheState( internal suspend fun DefaultWriteContext.writeBuildContent(build: VintageGradleBuild, buildTreeState: StoredBuildTreeState) { val gradle = build.gradle - val scheduledNodes = build.scheduledWork - withDebugFrame({ "Gradle" }) { - writeGradleState(gradle) - val projects = collectProjects(gradle.owner.projects, scheduledNodes, gradle.serviceOf()) - writeProjects(gradle, projects) - writeRequiredBuildServicesOf(gradle, buildTreeState) - } - withDebugFrame({ "Work Graph" }) { - writeWorkGraphOf(gradle, scheduledNodes) - } - withDebugFrame({ "Cleanup registrations" }) { - writeBuildOutputCleanupRegistrations(gradle) + val state = build.state + if (state.projectsAvailable) { + writeBoolean(true) + val scheduledNodes = build.scheduledWork + withDebugFrame({ "Gradle" }) { + writeGradleState(gradle) + val projects = collectProjects(state.projects, scheduledNodes, gradle.serviceOf()) + writeProjects(gradle, projects) + writeRequiredBuildServicesOf(gradle, buildTreeState) + } + withDebugFrame({ "Work Graph" }) { + writeWorkGraphOf(gradle, scheduledNodes) + } + withDebugFrame({ "Cleanup registrations" }) { + writeBuildOutputCleanupRegistrations(gradle) + } + } else { + writeBoolean(false) } } internal suspend fun DefaultReadContext.readBuildContent(build: ConfigurationCacheBuild): CachedBuildState { val gradle = build.gradle + if (readBoolean()) { + readGradleState(build) + val projects = readProjects(gradle, build) - readGradleState(build) - - val projects = readProjects(gradle, build) - - build.createProjects() + build.createProjects() - initProjectProvider(build::getProject) + initProjectProvider(build::getProject) - applyProjectStates(projects, gradle) - readRequiredBuildServicesOf(gradle) + applyProjectStates(projects, gradle) + readRequiredBuildServicesOf(gradle) - val workGraph = readWorkGraph(gradle) - readBuildOutputCleanupRegistrations(gradle) - return BuildWithWork(build.state.identityPath, build, gradle.rootProject.name, projects, workGraph) + val workGraph = readWorkGraph(gradle) + readBuildOutputCleanupRegistrations(gradle) + return BuildWithWork(build.state.identityPath, build, gradle.rootProject.name, projects, workGraph) + } else { + return BuildWithNoProjects(build.state.identityPath) + } } private @@ -546,10 +584,10 @@ class ConfigurationCacheState( private suspend fun DefaultWriteContext.writeCacheConfigurations(gradle: GradleInternal) { gradle.settings.caches.let { cacheConfigurations -> - write(cacheConfigurations.releasedWrappers) - write(cacheConfigurations.snapshotWrappers) - write(cacheConfigurations.downloadedResources) - write(cacheConfigurations.createdResources) + write(cacheConfigurations.releasedWrappers.removeUnusedEntriesOlderThan) + write(cacheConfigurations.snapshotWrappers.removeUnusedEntriesOlderThan) + write(cacheConfigurations.downloadedResources.removeUnusedEntriesOlderThan) + write(cacheConfigurations.createdResources.removeUnusedEntriesOlderThan) write(cacheConfigurations.cleanup) } } @@ -557,14 +595,21 @@ class ConfigurationCacheState( private suspend fun DefaultReadContext.readCacheConfigurations(gradle: GradleInternal) { gradle.settings.caches.let { cacheConfigurations -> - cacheConfigurations.releasedWrappers = read() as CacheResourceConfigurationInternal? - cacheConfigurations.snapshotWrappers = read() as CacheResourceConfigurationInternal? - cacheConfigurations.downloadedResources = read() as CacheResourceConfigurationInternal? - cacheConfigurations.createdResources = read() as CacheResourceConfigurationInternal? - cacheConfigurations.cleanup = readNonNull>() + readAndSetUnlockableProperty(cacheConfigurations.releasedWrappers.removeUnusedEntriesOlderThan) + readAndSetUnlockableProperty(cacheConfigurations.snapshotWrappers.removeUnusedEntriesOlderThan) + readAndSetUnlockableProperty(cacheConfigurations.downloadedResources.removeUnusedEntriesOlderThan) + readAndSetUnlockableProperty(cacheConfigurations.createdResources.removeUnusedEntriesOlderThan) + readAndSetUnlockableProperty(cacheConfigurations.cleanup) } } + private + suspend fun DefaultReadContext.readAndSetUnlockableProperty(property: UnlockableProperty) { + property.unlock() + property.value(readNonNull>()) + property.lock() + } + private suspend fun DefaultWriteContext.writeBuildEventListenerSubscriptions(listeners: List>) { writeCollection(listeners) { listener -> @@ -745,6 +790,10 @@ class ConfigurationCacheState( private fun BuildStateRegistry.buildServiceRegistrationOf(buildId: BuildIdentifier) = getBuild(buildId).mutableModel.serviceOf().registrations + + private + val BuildState.projectsAvailable + get() = isProjectsLoaded && projects.rootProject.isCreated } @@ -756,5 +805,5 @@ class StoredBuildTreeState( internal enum class BuildType { - BuildWithNoWork, RootBuild, IncludedBuild + BuildWithNoWork, RootBuild, IncludedBuild, BuildSrcBuild } 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 49450a8075fc..0706bc1183bd 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 @@ -74,10 +74,10 @@ class DefaultConfigurationCache internal constructor( val currentBuild: VintageGradleBuild - fun visitBuilds(visitor: (VintageGradleBuild) -> Unit) - fun createBuild(settingsFile: File?): ConfigurationCacheBuild + fun visitBuilds(visitor: (VintageGradleBuild) -> Unit) + fun service(serviceType: Class): T fun factory(serviceType: Class): Factory diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheConfigurationsInternal.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheConfigurationsInternal.java index 76493c12e44d..f61d579575e8 100644 --- a/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheConfigurationsInternal.java +++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheConfigurationsInternal.java @@ -37,13 +37,31 @@ public interface CacheConfigurationsInternal extends CacheConfigurations { @Override CacheResourceConfigurationInternal getCreatedResources(); - void setReleasedWrappers(CacheResourceConfigurationInternal releasedWrappers); - void setSnapshotWrappers(CacheResourceConfigurationInternal snapshotWrappers); - void setDownloadedResources(CacheResourceConfigurationInternal downloadedResources); - void setCreatedResources(CacheResourceConfigurationInternal createdResources); - void setCleanup(Property cleanup); + @Override + UnlockableProperty getCleanup(); - void finalizeConfigurations(); + /** + * Execute the provided runnable with all cache configuration properties unlocked and mutable. + */ + void withMutableValues(Runnable runnable); Provider getCleanupFrequency(); + + /** + * Represents a property that can be locked, preventing any changes that mutate the value. + * As opposed to finalization, the expectation is that the property may be unlocked + * again in the future. This allows properties that can only be changed during a certain + * window of time. + */ + interface UnlockableProperty extends Property { + /** + * Lock the property, preventing changes that mutate the value. + */ + void lock(); + + /** + * Unlock the property, allowing changes that mutate the value. + */ + void unlock(); + } } diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheResourceConfigurationInternal.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheResourceConfigurationInternal.java index 3efd3d17584e..9c8ddadf199b 100644 --- a/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheResourceConfigurationInternal.java +++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/cache/CacheResourceConfigurationInternal.java @@ -17,9 +17,11 @@ package org.gradle.api.internal.cache; import org.gradle.api.cache.CacheResourceConfiguration; - import java.util.function.Supplier; public interface CacheResourceConfigurationInternal extends CacheResourceConfiguration { Supplier getRemoveUnusedEntriesOlderThanAsSupplier(); + + @Override + CacheConfigurationsInternal.UnlockableProperty getRemoveUnusedEntriesOlderThan(); } diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptVisibilityIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptVisibilityIntegrationTest.groovy index 492495c942aa..d44a38d48169 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptVisibilityIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/SettingsScriptVisibilityIntegrationTest.groovy @@ -109,7 +109,7 @@ class SettingsScriptVisibilityIntegrationTest extends AbstractIntegrationSpec { """ when: - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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") succeeds("help", "-b", "other-build.gradle") then: diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsCompositeBuildTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsCompositeBuildTest.groovy new file mode 100644 index 000000000000..911e2e5a6a18 --- /dev/null +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsCompositeBuildTest.groovy @@ -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.api.internal.cache + +import org.gradle.integtests.fixtures.AbstractIntegrationSpec + + +class CacheConfigurationsCompositeBuildTest extends AbstractIntegrationSpec { + def "can configure cache retention with a composite build"() { + executer.requireOwnGradleUserHomeDir() + + def initDir = new File(executer.gradleUserHomeDir, "init.d") + initDir.mkdirs() + new File(initDir, "cache-settings.gradle") << """ + beforeSettings { settings -> + settings.caches { + cleanup = Cleanup.DISABLED + releasedWrappers.removeUnusedEntriesAfterDays = 10 + snapshotWrappers.removeUnusedEntriesAfterDays = 5 + downloadedResources.removeUnusedEntriesAfterDays = 10 + createdResources.removeUnusedEntriesAfterDays = 5 + } + } + """ + + file('foo').createDir().createFile('settings.gradle') + file('bar').createDir().createFile('settings.gradle') + settingsFile << """ + includeBuild('foo') + includeBuild('bar') + """ + + expect: + succeeds('help') + + and: + succeeds(':foo:tasks') + + and: + succeeds(':bar:tasks') + } +} diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsContinuousIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsContinuousIntegrationTest.groovy new file mode 100644 index 000000000000..cbbc4760ec0c --- /dev/null +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsContinuousIntegrationTest.groovy @@ -0,0 +1,60 @@ +/* + * 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.cache + +import org.gradle.integtests.fixtures.AbstractContinuousIntegrationTest + + +class CacheConfigurationsContinuousIntegrationTest extends AbstractContinuousIntegrationTest { + def "can configure caches via init script and execute multiple builds in a session"() { + executer.requireOwnGradleUserHomeDir() + + def initDir = new File(executer.gradleUserHomeDir, "init.d") + initDir.mkdirs() + new File(initDir, "cache-settings.gradle") << """ + beforeSettings { settings -> + settings.caches { + cleanup = Cleanup.DISABLED + releasedWrappers.removeUnusedEntriesAfterDays = 10 + snapshotWrappers.removeUnusedEntriesAfterDays = 5 + downloadedResources.removeUnusedEntriesAfterDays = 10 + createdResources.removeUnusedEntriesAfterDays = 5 + } + } + """ + + when: + buildFile << """ + task foo(type: Copy) { + from 'foo' + into layout.buildDir.dir('foo') + } + """ + file('foo').text = 'bar' + + then: + succeeds("foo") + file('build/foo/foo').text == 'bar' + + when: + file('foo').text = 'baz' + + then: + buildTriggeredAndSucceeded() + file('build/foo/foo').text == 'baz' + } +} diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsIntegrationTest.groovy index ec62ff8e69a2..04da3fc07112 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsIntegrationTest.groovy @@ -25,9 +25,6 @@ class CacheConfigurationsIntegrationTest extends AbstractIntegrationSpec { private static final int MODIFIED_AGE_IN_DAYS_FOR_DOWNLOADED_CACHE_ENTRIES = CacheConfigurationsInternal.DEFAULT_MAX_AGE_IN_DAYS_FOR_DOWNLOADED_CACHE_ENTRIES + 1 private static final int MODIFIED_AGE_IN_DAYS_FOR_CREATED_CACHE_ENTRIES = CacheConfigurationsInternal.DEFAULT_MAX_AGE_IN_DAYS_FOR_CREATED_CACHE_ENTRIES + 1 - private static final String THIS_PROPERTY_FINAL_ERROR = "The value for this property is final and cannot be changed any further" - private static final String REMOVE_UNUSED_ENTRIES_FINAL_ERROR = "The value for property 'removeUnusedEntriesOlderThan' is final and cannot be changed any further" - def setup() { requireOwnGradleUserHomeDir() } @@ -103,15 +100,15 @@ class CacheConfigurationsIntegrationTest extends AbstractIntegrationSpec { expect: fails("help") - failureCauseContains(error) + failureCauseContains(String.format(DefaultCacheConfigurations.ILLEGAL_MODIFICATION_ERROR, errorProperty)) where: - property | error | value - 'cleanup' | THIS_PROPERTY_FINAL_ERROR | 'Cleanup.DISABLED' - 'releasedWrappers.removeUnusedEntriesAfterDays' | REMOVE_UNUSED_ENTRIES_FINAL_ERROR | "${MODIFIED_AGE_IN_DAYS_FOR_RELEASED_DISTS}" - 'snapshotWrappers.removeUnusedEntriesAfterDays' | REMOVE_UNUSED_ENTRIES_FINAL_ERROR | "${MODIFIED_AGE_IN_DAY_FOR_SNAPSHOT_DISTS}" - 'downloadedResources.removeUnusedEntriesAfterDays' | REMOVE_UNUSED_ENTRIES_FINAL_ERROR | "${MODIFIED_AGE_IN_DAYS_FOR_DOWNLOADED_CACHE_ENTRIES}" - 'createdResources.removeUnusedEntriesAfterDays' | REMOVE_UNUSED_ENTRIES_FINAL_ERROR | "${MODIFIED_AGE_IN_DAYS_FOR_CREATED_CACHE_ENTRIES}" + property | errorProperty | value + 'cleanup' | 'cleanup' | 'Cleanup.DISABLED' + 'releasedWrappers.removeUnusedEntriesAfterDays' | 'removeUnusedEntriesOlderThan' | "${MODIFIED_AGE_IN_DAYS_FOR_RELEASED_DISTS}" + 'snapshotWrappers.removeUnusedEntriesAfterDays' | 'removeUnusedEntriesOlderThan' | "${MODIFIED_AGE_IN_DAY_FOR_SNAPSHOT_DISTS}" + 'downloadedResources.removeUnusedEntriesAfterDays' | 'removeUnusedEntriesOlderThan' | "${MODIFIED_AGE_IN_DAYS_FOR_DOWNLOADED_CACHE_ENTRIES}" + 'createdResources.removeUnusedEntriesAfterDays' | 'removeUnusedEntriesOlderThan' | "${MODIFIED_AGE_IN_DAYS_FOR_CREATED_CACHE_ENTRIES}" } static String modifyCacheConfiguration(String property, String value) { 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 de97c8cba519..0c7187025d96 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 @@ -119,7 +119,7 @@ class BuildResultLoggerIntegrationTest extends AbstractIntegrationSpec implement run "adHocTask" then: - result.assertTasksNotSkipped(":buildSrc:compileJava", ":buildSrc:jar", ":buildSrc:classes", ":buildSrc:assemble", ":buildSrc:build", ":adHocTask") + result.assertTasksNotSkipped(":buildSrc:compileJava", ":buildSrc:jar", ":buildSrc:classes", ":adHocTask") result.assertHasPostBuildOutput("3 actionable tasks: 3 executed") when: diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/GradleBuildTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/GradleBuildTaskIntegrationTest.groovy index f3bad0c5b9b5..b4f5c84a8ae0 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/GradleBuildTaskIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/GradleBuildTaskIntegrationTest.groovy @@ -100,7 +100,7 @@ class GradleBuildTaskIntegrationTest extends AbstractIntegrationSpec { println "other build file" ''' - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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"); when: run 'otherBuild' @@ -151,7 +151,7 @@ class GradleBuildTaskIntegrationTest extends AbstractIntegrationSpec { run 'otherBuild' then: - result.assertTaskExecuted(":other:buildSrc:assemble") + result.assertTaskExecuted(":other:buildSrc:jar") } def "buildSrc can have nested build"() { diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/LambdaInputsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/LambdaInputsIntegrationTest.groovy index 572ab3cf91aa..880761c2ce0d 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/LambdaInputsIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/LambdaInputsIntegrationTest.groovy @@ -18,10 +18,13 @@ package org.gradle.api.tasks import org.gradle.integtests.fixtures.AbstractIntegrationSpec import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture +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 org.junit.Assume +import spock.lang.Ignore import spock.lang.Issue class LambdaInputsIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker, DirectoryBuildCacheFixture { @@ -82,7 +85,12 @@ class LambdaInputsIntegrationTest extends AbstractIntegrationSpec implements Val ValidationProblemId.UNKNOWN_IMPLEMENTATION ) @Issue("https://github.com/gradle/gradle/issues/5510") + @Ignore("All lambdas are becoming serializable") + // TODO remove this test if the change making all lambdas serializable is not reverted def "task with nested property defined by non-serializable Java lambda fails the build"() { + // With configuration cache, all lambdas are forced to be serializable, so there won't be anything to report. + Assume.assumeTrue(GradleContextualExecuter.isNotConfigCache()) + setupTaskClassWithConsumerProperty() file("buildSrc/src/main/java/Lambdas.java") << javaClass("Lambdas", nonSerializableLambdaWritingFile("ACTION", "original")) 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 0880f6955de5..baa51c6c2265 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 @@ -199,7 +199,12 @@ class TaskCreationBuildOperationIntegrationTest extends AbstractIntegrationSpec """ } multiProjectBuild('root', ['sub'], createTasks) - includedBuild('buildSrc').multiProjectBuild('buildSrc', ['sub'], createTasks) + includedBuild('buildSrc').multiProjectBuild('buildSrc', ['sub']) { + buildFile << """ + allprojects { apply plugin: 'java-library' } + dependencies { implementation(project(":sub")) } + """ + } includedBuild('comp').multiProjectBuild('comp', ['sub'], createTasks).settingsFile buildFile << """ tasks.named('build').configure { it.dependsOn gradle.includedBuild('comp').task(':build') } @@ -211,8 +216,8 @@ class TaskCreationBuildOperationIntegrationTest extends AbstractIntegrationSpec then: verifyTaskIds() def expectedTasks = [ - withPath(':buildSrc', ':sub:build'), - withPath(':buildSrc', ':build'), + withPath(':buildSrc', ':sub:jar'), + withPath(':buildSrc', ':jar'), withPath(':comp', ':sub:build'), withPath(':comp', ':build'), withPath(':', ':sub:build'), diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ArchiveIntegrationTest.groovy index 3e893c1ef108..9e544aa02c97 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ArchiveIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ArchiveIntegrationTest.groovy @@ -923,7 +923,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { fcd.relativePath = new RelativePath(!fcd.isDirectory(), fcd.relativePath.segments.drop(1)) } } - into buildDir + into file("build/output") } """ @@ -931,7 +931,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { run "copy" then: - file("build").assertHasDescendants(expectedDescendants) + file("build/output").assertHasDescendants(expectedDescendants) where: includeEmptyDirs | expectedDescendants @@ -939,7 +939,6 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { false | ["file2.txt", "file3.txt"] } - @Issue("") def "zipTree tracks task dependencies"() { given: buildFile """ 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 new file mode 100644 index 000000000000..0f103f7afbf1 --- /dev/null +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy @@ -0,0 +1,563 @@ +/* + * 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.tasks.bundling + +import org.gradle.integtests.fixtures.AbstractIntegrationSpec +import org.gradle.integtests.fixtures.executer.GradleContextualExecuter +import org.gradle.test.fixtures.file.TestFile +import org.gradle.test.fixtures.server.http.BlockingHttpServer +import org.junit.Rule +import spock.lang.IgnoreIf +import spock.lang.Issue + +class ConcurrentArchiveIntegrationTest extends AbstractIntegrationSpec { + + @Rule + BlockingHttpServer server = new BlockingHttpServer() + + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit zip archive differently from two different projects in a multiproject build"() { + given: "an archive in the root of a multiproject build" + createZip('test.zip') { + subdir1 { + file ('file1.txt').text = 'original text 1' + } + subdir2 { + file('file2.txt').text = 'original text 2' + file ('file3.txt').text = 'original text 3' + } + } + settingsFile << """include 'project1', 'project2'""" + + and: "where each project edits that same archive differently via a visitor" + file('project1/build.gradle') << """ + ${defineUpdateTask('zip')} + ${defineVerifyTask('zip')} + + def theArchive = rootProject.file('test.zip') + + tasks.register('update', UpdateTask) { + archive = theArchive + replacementText = 'modified by project1' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = theArchive + beginsWith = 'modified by project1' + } + """ + + file('project2/build.gradle') << """ + ${defineUpdateTask('zip')} + ${defineVerifyTask('zip')} + + def theArchive = rootProject.file('test.zip') + + tasks.register('update', UpdateTask) { + archive = theArchive + replacementText = 'edited by project2' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = theArchive + beginsWith = 'edited by project2' + } + """ + + when: + run 'verify' + + then: + result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') + } + + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit tar archive differently from two different projects in a multiproject build"() { + given: "an archive in the root of a multiproject build" + createTar('test.tar') { + subdir1 { + file ('file1.txt').text = 'original text 1' + } + subdir2 { + file('file2.txt').text = 'original text 2' + file ('file3.txt').text = 'original text 3' + } + } + settingsFile << """include 'project1', 'project2'""" + + and: "where each project edits that same archive differently via a visitor" + file('project1/build.gradle') << """ + ${defineUpdateTask('tar')} + ${defineVerifyTask('tar')} + + def theArchive = rootProject.file('test.tar') + + tasks.register('update', UpdateTask) { + archive = theArchive + replacementText = 'modified by project1' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = theArchive + beginsWith = 'modified by project1' + } + """ + + file('project2/build.gradle') << """ + ${defineUpdateTask('tar')} + ${defineVerifyTask('tar')} + + def theArchive = rootProject.file('test.tar') + + tasks.register('update', UpdateTask) { + archive = theArchive + replacementText = 'edited by project2' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = theArchive + beginsWith = 'edited by project2' + } + """ + + when: + run 'verify' + + then: + result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') + } + + @IgnoreIf({ GradleContextualExecuter.embedded }) + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit zip archive differently from settings script when gradle is run in two simultaneous processes"() { + given: "a started server which can be used to cause the edits to begin at approximately the same time" + server.start() + + and: "a zip archive in the root with many files with the same content, so that the editing won't finish too quickly" + createZip('test.zip') { + for (int i = 0; i < 1000; i++) { + "subdir1$i" { + file('file1.txt').text = 'original' + } + "subdir2$i" { + file('file2.txt').text = 'original' + file('file3.txt').text = 'original' + } + } + } + + and: "a settings script which edits the zip archive and then verifies that all files contain the same content" + settingsFile << """ + ${server.callFromBuild('wait')} + + void update() { + FileTree tree = zipTree(new File(settings.rootDir, 'test.zip')) + tree.visit(new EditingFileVisitor()) + } + + final class EditingFileVisitor implements FileVisitor { + private final int num = new Random().nextInt(100000) + + @Override + void visitDir(FileVisitDetails dirDetails) {} + + @Override + void visitFile(FileVisitDetails fileDetails) { + fileDetails.file.text = fileDetails.file.text.replace('original', Integer.toString(num)) + } + } + + void verify() { + FileTree tree = zipTree(new File(settings.rootDir, 'test.zip')) + tree.visit(new VerifyingFileVisitor()) + } + + final class VerifyingFileVisitor implements FileVisitor { + private String contents + + @Override + void visitDir(FileVisitDetails dirDetails) {} + + @Override + void visitFile(FileVisitDetails fileDetails) { + if (contents == null) { + contents = fileDetails.file.text + } else { + assert fileDetails.file.text == contents + } + } + } + + update() + verify() + """ + + server.expectConcurrent('wait', 'wait') + + when: "two processes are started, which will edit the settings file at the same time" + def handle1 = executer.withTasks('tasks').start() + def handle2 = executer.withTasks('tasks').start() + + and: "they both complete" + def result1 = handle1.waitForFinish() + def result2 = handle2.waitForFinish() + + then: "both builds ran, with the settings script editing the archive atomically" + result1.assertTasksExecuted(':tasks') + result2.assertTasksExecuted(':tasks') + + cleanup: + handle1?.abort() + handle2?.abort() + server.stop() + } + + @IgnoreIf({ GradleContextualExecuter.embedded }) + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit tar archive differently from settings script when gradle is run in two simultaneous processes"() { + given: "a started server which can be used to cause the edits to begin at approximately the same time" + server.start() + + and: "a tar archive in the root with many files with the same content, so that the editing won't finish too quickly" + createTar('test.tar') { + for (int i = 0; i < 1000; i++) { + "subdir1$i" { + file('file1.txt').text = 'original' + } + "subdir2$i" { + file('file2.txt').text = 'original' + file('file3.txt').text = 'original' + } + } + } + + and: "a settings script which edits the tar archive and then verifies that all files contain the same content" + settingsFile << """ + ${server.callFromBuild('wait')} + + void update() { + FileTree tree = tarTree(new File(settings.rootDir, 'test.tar')) + tree.visit(new EditingFileVisitor()) + } + + final class EditingFileVisitor implements FileVisitor { + private final int num = new Random().nextInt(100000) + + @Override + void visitDir(FileVisitDetails dirDetails) {} + + @Override + void visitFile(FileVisitDetails fileDetails) { + fileDetails.file.text = fileDetails.file.text.replace('original', Integer.toString(num)) + } + } + + void verify() { + FileTree tree = tarTree(new File(settings.rootDir, 'test.tar')) + tree.visit(new VerifyingFileVisitor()) + } + + final class VerifyingFileVisitor implements FileVisitor { + private String contents + + @Override + void visitDir(FileVisitDetails dirDetails) {} + + @Override + void visitFile(FileVisitDetails fileDetails) { + if (contents == null) { + contents = fileDetails.file.text + } else { + assert fileDetails.file.text == contents + } + } + } + + update() + verify() + """ + + server.expectConcurrent('wait', 'wait') + + when: "two processes are started, which will edit the settings file at the same time" + def handle1 = executer.withTasks('tasks').start() + def handle2 = executer.withTasks('tasks').start() + + and: "they both complete" + def result1 = handle1.waitForFinish() + def result2 = handle2.waitForFinish() + + then: "both builds ran, with the settings script editing the archive atomically" + result1.assertTasksExecuted(':tasks') + result2.assertTasksExecuted(':tasks') + + cleanup: + handle1?.abort() + handle2?.abort() + server.stop() + } + + def "can operate on 2 different tar files in the same project"() { + given: "2 archive files" + createTar('test1.tar') { + subdir1 { + file ('file.txt').text = 'original text 1' + } + subdir2 { + file('file2.txt').text = 'original text 2' + file ('file3.txt').text = 'original text 3' + } + } + createTar('test2.tar') { + subdir1 { + file ('file.txt').text = 'original text 1' // Same name in same dir + } + subdir3 { + file('file3.txt').text = 'original text 3' // Same name in same different nested dir + file ('file4.txt').text = 'original text 4' + } + } + + and: "where a build edits each archive differently via a visitor" + file('build.gradle') << """ + ${defineUpdateTask('tar')} + ${defineVerifyTask('tar')} + + def theArchive1 = rootProject.file('test1.tar') + def theArchive2 = rootProject.file('test2.tar') + + tasks.register('update1', UpdateTask) { + archive = theArchive1 + replacementText = 'modification 1' + } + + tasks.register('verify1', VerifyTask) { + dependsOn tasks.named('update1') + archive = theArchive1 + beginsWith = 'modification 1' + } + + tasks.register('update2', UpdateTask) { + archive = theArchive2 + replacementText = 'modification 2' + } + + tasks.register('verify2', VerifyTask) { + dependsOn tasks.named('update2') + archive = theArchive2 + beginsWith = 'modification 2' + } + """ + + when: + run 'verify1', 'verify2' + + then: + result.assertTasksExecutedAndNotSkipped(':update1', ':update2', ':verify1', ':verify2') + } + + def "can operate on 2 different zip files in the same project"() { + given: "2 archive files" + createZip('test1.zip') { + subdir1 { + file ('file.txt').text = 'original text 1' + } + subdir2 { + file('file2.txt').text = 'original text 2' + file ('file3.txt').text = 'original text 3' + } + } + createZip('test2.zip') { + subdir1 { + file ('file.txt').text = 'original text 1' // Same name in same dir + } + subdir3 { + file('file3.txt').text = 'original text 3' // Same name in same different nested dir + file ('file4.txt').text = 'original text 4' + } + } + + and: "where a build edits each archive differently via a visitor" + file('build.gradle') << """ + ${defineUpdateTask('zip')} + ${defineVerifyTask('zip')} + + def theArchive1 = rootProject.file('test1.zip') + def theArchive2 = rootProject.file('test2.zip') + + tasks.register('update1', UpdateTask) { + archive = theArchive1 + replacementText = 'modification 1' + } + + tasks.register('verify1', VerifyTask) { + dependsOn tasks.named('update1') + archive = theArchive1 + beginsWith = 'modification 1' + } + + tasks.register('update2', UpdateTask) { + archive = theArchive2 + replacementText = 'modification 2' + } + + tasks.register('verify2', VerifyTask) { + dependsOn tasks.named('update2') + archive = theArchive2 + beginsWith = 'modification 2' + } + """ + + when: + run 'verify1', 'verify2' + + then: + result.assertTasksExecutedAndNotSkipped(':update1', ':update2', ':verify1', ':verify2') + } + + def "when two identical archives have the same hashes and same decompression cache entry is reused"() { + given: "2 archive files" + createTar('test1.tar') { + subdir1 { + file ('file.txt').text = 'original text 1' + } + subdir2 { + file('file2.txt').text = 'original text 2' + 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' + } + } + + and: "where a build edits each archive differently via a visitor" + file('build.gradle') << """ + ${defineUpdateTask('tar')} + ${defineVerifyTask('tar')} + + def theArchive1 = rootProject.file('test1.tar') + def theArchive2 = rootProject.file('test2.tar') + + tasks.register('update1', UpdateTask) { + archive = theArchive1 + replacementText = 'modification 1' + } + + tasks.register('update2', UpdateTask) { + archive = theArchive2 + replacementText = 'modification 2' + } + + tasks.register('verify') { + dependsOn tasks.named('update1'), tasks.named('update2') + doLast { + def cacheDir = file("build/tmp/.cache/expanded") + assert cacheDir.list().size() == 2 // There should only be 2 files here, the .lock file and the single unzipped cache entry + assert cacheDir.list().contains('expanded.lock') + cacheDir.eachFile(groovy.io.FileType.DIRECTORIES) { File f -> + assert f.name.startsWith('tar_') + } + } + } + """ + + when: + succeeds 'verify' + + then: + result.assertTasksExecutedAndNotSkipped(':update1', ':update2', ':verify') + } + + private def createTar(String name, Closure cl) { + TestFile tarRoot = file("${name}.root") + tarRoot.deleteDir() + TestFile tar = file(name) + tar.delete() + tarRoot.create(cl) + tarRoot.tarTo(tar) + } + + private String defineUpdateTask(String archiveType) { + return """ + abstract class UpdateTask extends DefaultTask { + @InputFile + abstract RegularFileProperty getArchive() + + @Input + abstract Property getReplacementText() + + @Inject abstract ArchiveOperations getArchiveOperations() + + @TaskAction + void update() { + FileTree tree = archiveOperations.${archiveType}Tree(archive.asFile.get()) + tree.visit(new EditingFileVisitor()) + } + + private final class EditingFileVisitor implements FileVisitor { + @Override + void visitDir(FileVisitDetails dirDetails) {} + + @Override + void visitFile(FileVisitDetails fileDetails) { + fileDetails.file.text = fileDetails.file.text.replace('original', replacementText.get()) + } + } + } + """ + } + + private String defineVerifyTask(String archiveType) { + return """ + abstract class VerifyTask extends DefaultTask { + @InputFile + abstract RegularFileProperty getArchive() + + @Input + abstract Property getBeginsWith() + + @Inject abstract ArchiveOperations getArchiveOperations() + + @TaskAction + void verify() { + FileTree tree = archiveOperations.${archiveType}Tree(archive.asFile.get()) + tree.visit(new VerifyingFileVisitor()) + } + + private final class VerifyingFileVisitor implements FileVisitor { + @Override + void visitDir(FileVisitDetails dirDetails) {} + + @Override + void visitFile(FileVisitDetails fileDetails) { + assert fileDetails.file.text.startsWith(beginsWith.get()) + } + } + } + """ + } +} diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/outputorigin/IncrementalBuildOutputOriginIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/outputorigin/IncrementalBuildOutputOriginIntegrationTest.groovy index 5dc807e2cfee..adac63460b17 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/outputorigin/IncrementalBuildOutputOriginIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/outputorigin/IncrementalBuildOutputOriginIntegrationTest.groovy @@ -142,7 +142,7 @@ class IncrementalBuildOutputOriginIntegrationTest extends AbstractIntegrationSpe outputFile = "w.properties" properties = [v: 1] } - build.dependsOn "w" + jar.dependsOn "w" """ when: 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 167f34e92dba..5b80b425e2fe 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 @@ -69,11 +69,11 @@ class BuildCacheCompositeConfigurationIntegrationTest extends AbstractIntegratio buildFile << customTaskCode("root") file("buildSrc/build.gradle") << customTaskCode("buildSrc") << """ - build.dependsOn customTask + jar.dependsOn customTask """ file("i1/build.gradle") << customTaskCode("i1") file("i1/buildSrc/build.gradle") << customTaskCode("i1:buildSrc") << """ - build.dependsOn customTask + jar.dependsOn customTask """ file("i2/build.gradle") << customTaskCode("i2") if (isNotConfigCache()) { // GradleBuild is not supported with the configuration cache diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/CalculateTaskGraphBuildOperationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/CalculateTaskGraphBuildOperationIntegrationTest.groovy index 5bd32dbcce0c..2cacfda2f9c9 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/CalculateTaskGraphBuildOperationIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/CalculateTaskGraphBuildOperationIntegrationTest.groovy @@ -139,7 +139,7 @@ class CalculateTaskGraphBuildOperationIntegrationTest extends AbstractIntegratio then: taskGraphCalculations.size() == 3 taskGraphCalculations[0].details.buildPath == ":buildSrc" - taskGraphCalculations[0].result.requestedTaskPaths == [":build"] + taskGraphCalculations[0].result.requestedTaskPaths == [":jar"] taskGraphCalculations[1].details.buildPath == ":" taskGraphCalculations[1].result.requestedTaskPaths == [":build"] taskGraphCalculations[2].details.buildPath == ":b" diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/EvaluateSettingsBuildOperationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/EvaluateSettingsBuildOperationIntegrationTest.groovy index 3ea0fbc39cd7..3394c76481be 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/EvaluateSettingsBuildOperationIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/EvaluateSettingsBuildOperationIntegrationTest.groovy @@ -45,7 +45,7 @@ class EvaluateSettingsBuildOperationIntegrationTest extends AbstractIntegrationS """ when: - executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 8.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: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout") executer.withArguments("--settings-file", customSettingsFile.absolutePath) succeeds('help') diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/InitScriptIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/InitScriptIntegrationTest.groovy index a9f069a72646..848154654726 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/InitScriptIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/InitScriptIntegrationTest.groovy @@ -16,7 +16,7 @@ package org.gradle.initialization -import groovy.test.NotYetImplemented + import org.gradle.api.Plugin import org.gradle.api.initialization.Settings import org.gradle.integtests.fixtures.AbstractIntegrationSpec @@ -48,7 +48,6 @@ class InitScriptIntegrationTest extends AbstractIntegrationSpec { """ } - @NotYetImplemented @Issue(['GRADLE-1457', 'GRADLE-3197']) def 'init scripts passed on the command line are applied to buildSrc'() { given: diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/LoadBuildStructureBuildOperationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/LoadBuildStructureBuildOperationIntegrationTest.groovy index 893deccd1fea..d9024f2e42b1 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/LoadBuildStructureBuildOperationIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/LoadBuildStructureBuildOperationIntegrationTest.groovy @@ -77,7 +77,7 @@ class LoadBuildStructureBuildOperationIntegrationTest extends AbstractIntegratio """ when: - executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 8.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: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout") executer.usingSettingsFile(customSettingsFile) succeeds('help') diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcEventsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcEventsIntegrationTest.groovy index dd4e27af3c7f..28142dea2c0e 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcEventsIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcEventsIntegrationTest.groovy @@ -21,7 +21,7 @@ import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache class BuildSrcEventsIntegrationTest extends AbstractIntegrationSpec { @UnsupportedWithConfigurationCache(because = "uses buildFinished") - def "buildSrc build finished hook is executed prior to configuring root build"() { + def "buildSrc build finished hook is executed after running main tasks and before root build build finished hook"() { file("buildSrc/build.gradle") << """ System.clearProperty("buildsrc") @@ -31,15 +31,25 @@ class BuildSrcEventsIntegrationTest extends AbstractIntegrationSpec { } """ file("build.gradle") << """ - println "configuring root project" - assert System.getProperty("buildsrc") == "done" + task thing { + doLast { + println("running task") + assert System.getProperty("buildsrc") == null + } + } + + gradle.buildFinished { + println "root build finished" + assert System.getProperty("buildsrc") == "done" + } """ when: run() then: - output.indexOf("buildSrc finished") < output.indexOf("configuring root project") + output.indexOf("running tasks") < output.indexOf("buildSrc finished") + output.indexOf("buildSrc finished") < output.indexOf("root build finished") } @UnsupportedWithConfigurationCache(because = "uses buildFinished") @@ -50,15 +60,12 @@ class BuildSrcEventsIntegrationTest extends AbstractIntegrationSpec { throw new RuntimeException("broken") } """ - settingsFile << """ + buildFile << """ gradle.buildFinished { result -> println "root build finished" assert result.failure != null } """ - buildFile << """ - throw new RuntimeException("should not happen") - """ when: fails() @@ -76,16 +83,13 @@ class BuildSrcEventsIntegrationTest extends AbstractIntegrationSpec { throw new RuntimeException("buildSrc") } """ - settingsFile << """ + buildFile << """ gradle.buildFinished { result -> println "root build finished" assert result.failure != null throw new RuntimeException("root build") } """ - buildFile << """ - throw new RuntimeException("should not happen") - """ when: fails() diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcGradlePluginApiVersionAttributeIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcGradlePluginApiVersionAttributeIntegrationTest.groovy index 4f2fb92c9280..529db7d26c0f 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcGradlePluginApiVersionAttributeIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcGradlePluginApiVersionAttributeIntegrationTest.groovy @@ -54,19 +54,22 @@ class BuildSrcGradlePluginApiVersionAttributeIntegrationTest extends AbstractInt } } - build.dependsOn(task) - build.dependsOn(":sub:checkConfigurations") + + tasks.register("verify") { + dependsOn(task) + dependsOn(":sub:checkConfigurations") + } """ when: - succeeds("help") + succeeds(":buildSrc:verify") then: def configurationsWithAttribute = output.findAll(">>> .*").collect {it.takeAfter(">>> ").split("=") }.collectEntries() configurationsWithAttribute == - ["compileClasspath", "runtimeClasspath", "testCompileClasspath", "testRuntimeClasspath", "otherCompileClasspath", "otherRuntimeClasspath"] + ["buildScriptClasspath", "compileClasspath", "runtimeClasspath", "testCompileClasspath", "testRuntimeClasspath", "otherCompileClasspath", "otherRuntimeClasspath"] .collectEntries { [it, GradleVersion.current().getVersion()] } } } diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIncludedBuildIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIncludedBuildIntegrationTest.groovy index 93284f24c8f7..cf9593fbfb7e 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIncludedBuildIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIncludedBuildIntegrationTest.groovy @@ -22,20 +22,108 @@ import org.gradle.test.fixtures.file.TestFile import org.gradle.test.fixtures.plugin.PluginBuilder class BuildSrcIncludedBuildIntegrationTest extends AbstractIntegrationSpec { - def "buildSrc cannot (yet) define any included builds"() { + def "buildSrc can use a library contributed by a build that it includes"() { file("buildSrc/settings.gradle") << """ - includeBuild "child" + includeBuild("../included") """ - file("buildSrc/child/settings.gradle").createFile() + file("buildSrc/build.gradle") << """ + dependencies { + implementation("test.lib:lib:1.0") + } + """ + writeLibraryTo(file("included")) + useLibraryFrom(file("buildSrc")) + + when: + run() + + then: + result.assertTaskExecuted(":included:jar") + result.assertTaskExecuted(":buildSrc:jar") + } + + def "buildSrc can use a library contributed by a build included by the root build"() { + file("buildSrc/build.gradle") << """ + dependencies { + implementation("test.lib:lib:1.0") + } + """ + writeLibraryTo(file("included")) + useLibraryFrom(file("buildSrc")) + + settingsFile << """ + includeBuild("included") + """ + + when: + run() + + then: + result.assertTaskExecuted(":included:jar") + result.assertTaskExecuted(":buildSrc:jar") + } + + // buildSrc acts like an implicit pluginManagement { } included build + def "library contributed by buildSrc is not visible to the root build"() { + writeLibraryTo(file("buildSrc")) + + file("build.gradle") << """ + plugins { id("java-library") } + dependencies { + implementation("test.lib:lib:1.0") + } + """ + useLibraryFrom(testDirectory) when: - fails() + fails("build") then: - failure.assertHasDescription("Cannot include build 'child' in build 'buildSrc'. This is not supported yet.") + failure.assertTaskExecuted(":buildSrc:jar") + failure.assertHasCause("Cannot resolve external dependency test.lib:lib:1.0 because no repositories are defined.") + } + + // buildSrc acts like an implicit pluginManagement { } included build + def "library contributed by buildSrc is not visible to a build included by the root build"() { + writeLibraryTo(file("buildSrc")) + + settingsFile << """ + includeBuild("included") + """ + file("included/build.gradle") << """ + plugins { id("java-library") } + dependencies { + implementation("test.lib:lib:1.0") + } + """ + useLibraryFrom(file("included")) + + when: + fails(":included:build") + + then: + failure.assertTaskExecuted(":buildSrc:jar") + failure.assertHasCause("Cannot resolve external dependency test.lib:lib:1.0 because no repositories are defined.") + } + + def "buildSrc can apply plugins contributed by a build that it includes"() { + file("buildSrc/settings.gradle") << """ + includeBuild("../included") + """ + file("buildSrc/build.gradle") << """ + plugins { + id "test-plugin" + } + """ + writePluginTo(file("included")) + + when: + succeeds("help") + then: + outputContains("test-plugin applied to :buildSrc") } - def "buildSrc can apply plugins contributed by other included builds"() { + def "buildSrc can apply plugins contributed by a build included by the root build"() { file("buildSrc/build.gradle") << """ plugins { id "test-plugin" @@ -53,7 +141,25 @@ class BuildSrcIncludedBuildIntegrationTest extends AbstractIntegrationSpec { outputContains("test-plugin applied to :buildSrc") } - def "buildSrc can apply plugins contributed by other included builds from CLI"() { + def "plugin contributes by buildSrc are not visible to a build included by the root build"() { + writePluginTo(file("buildSrc")) + + settingsFile << """ + includeBuild("included") + """ + file("included/build.gradle") << """ + plugins { + id "test-plugin" + } + + """ + when: + fails("help") + then: + failure.assertHasDescription("Plugin [id: 'test-plugin'] was not found in any of the following sources") + } + + def "buildSrc can apply plugins contributed by a build included from CLI"() { file("buildSrc/build.gradle") << """ plugins { id "test-plugin" @@ -67,7 +173,46 @@ class BuildSrcIncludedBuildIntegrationTest extends AbstractIntegrationSpec { outputContains("test-plugin applied to :buildSrc") } - def "buildSrc can apply settings plugins contributed by other included builds"() { + def "buildSrc can apply plugins contributed by a build that it includes via pluginManagement"() { + file("buildSrc/settings.gradle") << """ + pluginManagement { + includeBuild("../included") + } + """ + file("buildSrc/build.gradle") << """ + plugins { + id "test-plugin" + } + """ + writePluginTo(file("included")) + + when: + succeeds("help") + then: + outputContains("test-plugin applied to :buildSrc") + } + + def "buildSrc can apply settings plugins contributed by a build it includes via pluginManagement"() { + file("buildSrc/settings.gradle") << """ + pluginManagement { + includeBuild("../included") + } + plugins { + id "test-settings-plugin" + } + """ + + def pluginBuilder = new PluginBuilder(file("included")) + pluginBuilder.addSettingsPlugin("println 'test-settings-plugin applied to ' + settings.gradle.publicBuildPath.buildPath") + pluginBuilder.prepareToExecute() + + when: + succeeds("help") + then: + outputContains("test-settings-plugin applied to :buildSrc") + } + + def "buildSrc can apply settings plugins contributed by a build included by the root build"() { file("buildSrc/settings.gradle") << """ plugins { id "test-settings-plugin" @@ -108,7 +253,7 @@ class BuildSrcIncludedBuildIntegrationTest extends AbstractIntegrationSpec { outputContains("test-plugin applied to :buildSrc") } - def "buildSrc can depend on dependencies contributed by other included builds"() { + def "buildSrc can depend on plugins contributed by a build included by the root build"() { file("buildSrc/build.gradle") << """ plugins { id "groovy-gradle-plugin" @@ -176,7 +321,7 @@ class BuildSrcIncludedBuildIntegrationTest extends AbstractIntegrationSpec { failure.assertHasCause("Compilation failed; see the compiler error output for details.") } - def "buildSrc can apply plugins contributed by other included builds and use them in plugins for the root build"() { + def "buildSrc can apply plugins contributed by a build included by the root build and use them in plugins for the root build"() { file("buildSrc/build.gradle") << """ plugins { id "test-plugin" @@ -217,4 +362,27 @@ class BuildSrcIncludedBuildIntegrationTest extends AbstractIntegrationSpec { pluginBuilder.prepareToExecute() } + private void writeLibraryTo(TestFile projectDir) { + projectDir.file("settings.gradle") << """ + rootProject.name = "lib" + """ + projectDir.file("build.gradle") << """ + plugins { id("java-library") } + + group = "test.lib" + """ + projectDir.file("src/main/java/lib/Lib.java") << """ + package lib; + public class Lib { } + """ + } + + private void useLibraryFrom(TestFile projectDir) { + projectDir.file("src/main/java/Consumer.java") << """ + import lib.Lib; + class Consumer { + Lib lib = new Lib(); + } + """ + } } diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcLocationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcLocationIntegrationTest.groovy index ca60da214d7d..b6af56efe102 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcLocationIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcLocationIntegrationTest.groovy @@ -29,8 +29,8 @@ class BuildSrcLocationIntegrationTest extends AbstractIntegrationSpec { def movedBuildScript = file("root/build.gradle") << "" - executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build file location has been deprecated. This is scheduled to be removed in Gradle 8.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: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout") + executer.expectDocumentedDeprecationWarning("Specifying custom build 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") args('help', "-b", movedBuildScript.absolutePath, "-c", settingsFile.absolutePath) succeeds("help") diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcTaskExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcTaskExecutionIntegrationTest.groovy new file mode 100644 index 000000000000..9fb3d09ed466 --- /dev/null +++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcTaskExecutionIntegrationTest.groovy @@ -0,0 +1,91 @@ +/* + * 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.initialization.buildsrc + +import org.gradle.integtests.fixtures.AbstractIntegrationSpec +import org.gradle.util.internal.ToBeImplemented + +class BuildSrcTaskExecutionIntegrationTest extends AbstractIntegrationSpec { + def "can execute a task from buildSrc from the command line"() { + file("buildSrc/build.gradle") << """ + task something { + doLast { } + } + """ + + expect: + 2.times { + run(":buildSrc:something") + result.assertTaskExecuted(":buildSrc:something") + } + + run(":buildSrc:jar") + result.assertTaskExecuted(":buildSrc:jar") + + run(":buildSrc:jar") + // Task will not run when configuration cache is enabled + } + + def "can execute a task from nested buildSrc from the command line"() { + file("settings.gradle") << """ + includeBuild("nested") + """ + file("nested/buildSrc/build.gradle") << """ + task something { + doLast { } + } + """ + + expect: + 2.times { + run(":nested:buildSrc:something") + result.assertTaskExecuted(":nested:buildSrc:something") + } + + run(":nested:buildSrc:jar") + result.assertTaskExecuted(":nested:buildSrc:jar") + + run(":nested:buildSrc:jar") + // Task will not run when configuration cache is enabled + } + + @ToBeImplemented + def "can exclude a task from buildSrc from the command line"() { + file("buildSrc/build.gradle") << """ + task something { + doLast { } + } + task thing { + dependsOn something + } + """ + + expect: + 2.times { + run(":buildSrc:thing", "-x", ":buildSrc:something") + result.assertTaskExecuted(":buildSrc:thing") + result.assertTaskNotExecuted(":buildSrc:something") + } + + 2.times { + run("-x", ":buildSrc:jar") + // TODO - should exclude these tasks, for consistency with included builds +// result.assertTaskNotExecuted(":buildSrc:compileJava") +// result.assertTaskNotExecuted(":buildSrc:jar") + } + } +} diff --git a/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/logging/LoggingBuildOperationProgressIntegTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/logging/LoggingBuildOperationProgressIntegTest.groovy index 06abb8ffdb7d..a284da9c744b 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/logging/LoggingBuildOperationProgressIntegTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/logging/LoggingBuildOperationProgressIntegTest.groovy @@ -227,7 +227,7 @@ class LoggingBuildOperationProgressIntegTest extends AbstractIntegrationSpec { def "captures output from buildSrc"() { given: configureNestedBuild('buildSrc') - file('buildSrc/build.gradle') << "build.dependsOn 'foo'" + file('buildSrc/build.gradle') << "jar.dependsOn 'foo'" file("build.gradle") << "" when: diff --git a/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/notify/BuildOperationNotificationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/notify/BuildOperationNotificationIntegrationTest.groovy index 110c730871b5..a41a9bceeb19 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/notify/BuildOperationNotificationIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/internal/operations/notify/BuildOperationNotificationIntegrationTest.groovy @@ -293,6 +293,6 @@ class BuildOperationNotificationIntegrationTest extends AbstractIntegrationSpec then: result.assertTaskExecuted(":buildSrc:compileJava") notifications.recordedOps.findAll { it.detailsType == ConfigureProjectBuildOperationType.Details.name }.size() == 2 - notifications.recordedOps.findAll { it.detailsType == ExecuteTaskBuildOperationType.Details.name }.size() == 14 // including all buildSrc task execution events + notifications.recordedOps.findAll { it.detailsType == ExecuteTaskBuildOperationType.Details.name }.size() == 6 // including all buildSrc task execution events } } 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 bc8872e94c3e..42fb04c85e14 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 @@ -19,31 +19,46 @@ import org.gradle.api.Action; import org.gradle.api.cache.CacheResourceConfiguration; import org.gradle.api.cache.Cleanup; +import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.provider.DefaultProperty; import org.gradle.api.internal.provider.DefaultProvider; +import org.gradle.api.internal.provider.PropertyHost; import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.cache.CleanupFrequency; +import org.gradle.internal.Describables; +import org.gradle.internal.DisplayName; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; import javax.inject.Inject; import java.util.function.Supplier; import static org.gradle.internal.time.TimestampSuppliers.daysAgo; abstract public class DefaultCacheConfigurations implements CacheConfigurationsInternal { - private CacheResourceConfigurationInternal releasedWrappersConfiguration; - private CacheResourceConfigurationInternal snapshotWrappersConfiguration; - private CacheResourceConfigurationInternal downloadedResourcesConfiguration; - private CacheResourceConfigurationInternal createdResourcesConfiguration; - private Property cleanup; + private static final DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry(); + private static final String RELEASED_WRAPPERS = "releasedWrappers"; + private static final String SNAPSHOT_WRAPPERS = "snapshotWrappers"; + private static final String DOWNLOADED_RESOURCES = "downloadedResources"; + private static final String CREATED_RESOURCES = "createdResources"; + static final String ILLEGAL_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."; + + private final CacheResourceConfigurationInternal releasedWrappersConfiguration; + private final CacheResourceConfigurationInternal snapshotWrappersConfiguration; + private final CacheResourceConfigurationInternal downloadedResourcesConfiguration; + private final CacheResourceConfigurationInternal createdResourcesConfiguration; + private final UnlockableProperty cleanup; @Inject - public DefaultCacheConfigurations(ObjectFactory objectFactory) { - this.releasedWrappersConfiguration = createResourceConfiguration(objectFactory, "releasedWrappers", DEFAULT_MAX_AGE_IN_DAYS_FOR_RELEASED_DISTS); - this.snapshotWrappersConfiguration = createResourceConfiguration(objectFactory, "snapshotWrappers", DEFAULT_MAX_AGE_IN_DAYS_FOR_SNAPSHOT_DISTS); - this.downloadedResourcesConfiguration = createResourceConfiguration(objectFactory, "downloadedResources", DEFAULT_MAX_AGE_IN_DAYS_FOR_DOWNLOADED_CACHE_ENTRIES); - this.createdResourcesConfiguration = createResourceConfiguration(objectFactory, "createdResources", DEFAULT_MAX_AGE_IN_DAYS_FOR_CREATED_CACHE_ENTRIES); - this.cleanup = objectFactory.property(Cleanup.class).convention(Cleanup.DEFAULT); + public DefaultCacheConfigurations(ObjectFactory objectFactory, PropertyHost propertyHost) { + this.releasedWrappersConfiguration = createResourceConfiguration(objectFactory, RELEASED_WRAPPERS, DEFAULT_MAX_AGE_IN_DAYS_FOR_RELEASED_DISTS); + this.snapshotWrappersConfiguration = createResourceConfiguration(objectFactory, SNAPSHOT_WRAPPERS, DEFAULT_MAX_AGE_IN_DAYS_FOR_SNAPSHOT_DISTS); + this.downloadedResourcesConfiguration = createResourceConfiguration(objectFactory, DOWNLOADED_RESOURCES, DEFAULT_MAX_AGE_IN_DAYS_FOR_DOWNLOADED_CACHE_ENTRIES); + this.createdResourcesConfiguration = createResourceConfiguration(objectFactory, CREATED_RESOURCES, DEFAULT_MAX_AGE_IN_DAYS_FOR_CREATED_CACHE_ENTRIES); + this.cleanup = new DefaultUnlockableProperty<>(propertyHost, Cleanup.class, "cleanup").convention(Cleanup.DEFAULT); + lockValues(); } private static CacheResourceConfigurationInternal createResourceConfiguration(ObjectFactory objectFactory, String name, int defaultDays) { @@ -62,11 +77,6 @@ public CacheResourceConfigurationInternal getReleasedWrappers() { return releasedWrappersConfiguration; } - @Override - public void setReleasedWrappers(CacheResourceConfigurationInternal releasedWrappers) { - this.releasedWrappersConfiguration = releasedWrappers; - } - @Override public void snapshotWrappers(Action cacheConfiguration) { cacheConfiguration.execute(snapshotWrappersConfiguration); @@ -77,11 +87,6 @@ public CacheResourceConfigurationInternal getSnapshotWrappers() { return snapshotWrappersConfiguration; } - @Override - public void setSnapshotWrappers(CacheResourceConfigurationInternal snapshotWrappers) { - this.snapshotWrappersConfiguration = snapshotWrappers; - } - @Override public void downloadedResources(Action cacheConfiguration) { cacheConfiguration.execute(downloadedResourcesConfiguration); @@ -92,11 +97,6 @@ public CacheResourceConfigurationInternal getDownloadedResources() { return downloadedResourcesConfiguration; } - @Override - public void setDownloadedResources(CacheResourceConfigurationInternal downloadedResources) { - this.downloadedResourcesConfiguration = downloadedResources; - } - @Override public void createdResources(Action cacheConfiguration) { cacheConfiguration.execute(createdResourcesConfiguration); @@ -108,32 +108,39 @@ public CacheResourceConfigurationInternal getCreatedResources() { } @Override - public void setCreatedResources(CacheResourceConfigurationInternal createdResources) { - this.createdResourcesConfiguration = createdResources; + public UnlockableProperty getCleanup() { + return cleanup; } @Override - public Property getCleanup() { - return cleanup; + public Provider getCleanupFrequency() { + return getCleanup().map(cleanup -> ((CleanupInternal)cleanup).getCleanupFrequency()); } @Override - public void setCleanup(Property cleanup) { - this.cleanup = cleanup; + public void withMutableValues(Runnable runnable) { + unlockValues(); + try { + runnable.run(); + } finally { + lockValues(); + } } - @Override - public Provider getCleanupFrequency() { - return getCleanup().map(cleanup -> ((CleanupInternal)cleanup).getCleanupFrequency()); + private void lockValues() { + releasedWrappersConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + snapshotWrappersConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + downloadedResourcesConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + createdResourcesConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + getCleanup().lock(); } - @Override - public void finalizeConfigurations() { - releasedWrappersConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - snapshotWrappersConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - downloadedResourcesConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - createdResourcesConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - getCleanup().finalizeValue(); + private void unlockValues() { + releasedWrappersConfiguration.getRemoveUnusedEntriesOlderThan().unlock(); + snapshotWrappersConfiguration.getRemoveUnusedEntriesOlderThan().unlock(); + downloadedResourcesConfiguration.getRemoveUnusedEntriesOlderThan().unlock(); + createdResourcesConfiguration.getRemoveUnusedEntriesOlderThan().unlock(); + getCleanup().unlock(); } private static Provider providerFromSupplier(Supplier supplier) { @@ -142,10 +149,17 @@ private static Provider providerFromSupplier(Supplier supplier) { static abstract class DefaultCacheResourceConfiguration implements CacheResourceConfigurationInternal { private final String name; + private final UnlockableProperty removeUnusedEntriesOlderThan; @Inject - public DefaultCacheResourceConfiguration(String name) { + public DefaultCacheResourceConfiguration(PropertyHost propertyHost, String name) { this.name = name; + this.removeUnusedEntriesOlderThan = new DefaultUnlockableProperty<>(propertyHost, Long.class, "removeUnusedEntriesOlderThan"); + } + + @Override + public UnlockableProperty getRemoveUnusedEntriesOlderThan() { + return removeUnusedEntriesOlderThan; } /** @@ -166,4 +180,82 @@ public void setRemoveUnusedEntriesAfterDays(int removeUnusedEntriesAfterDays) { getRemoveUnusedEntriesOlderThan().set(providerFromSupplier(daysAgo(removeUnusedEntriesAfterDays))); } } + + /** + * A non-threadsafe implementation of {@link org.gradle.api.internal.cache.CacheConfigurationsInternal.UnlockableProperty}. + */ + @NotThreadSafe + static class DefaultUnlockableProperty extends DefaultProperty implements UnlockableProperty { + private final String displayName; + private boolean mutable = true; + + public DefaultUnlockableProperty(PropertyHost propertyHost, Class type, String displayName) { + super(propertyHost, type); + this.displayName = displayName; + } + + public void lock() { + mutable = false; + } + + public void unlock() { + mutable = true; + } + + private IllegalStateException lockedError() { + return new IllegalStateException(String.format(ILLEGAL_MODIFICATION_ERROR, getDisplayName())); + } + + private void onlyIfMutable(Runnable runnable) { + if (mutable) { + runnable.run(); + } else { + throw lockedError(); + } + } + + @Override + @Nonnull + protected DisplayName getDisplayName() { + if (displayName != null) { + return Describables.of(displayName); + } else { + return super.getDisplayName(); + } + } + + @Override + public void set(@Nullable T value) { + onlyIfMutable(() -> super.set(value)); + } + + @Override + public void set(Provider provider) { + onlyIfMutable(() -> super.set(provider)); + } + + @Override + public DefaultUnlockableProperty value(@Nullable T value) { + onlyIfMutable(() -> super.value(value)); + return this; + } + + @Override + public DefaultUnlockableProperty value(Provider provider) { + onlyIfMutable(() -> super.value(provider)); + return this; + } + + @Override + public DefaultUnlockableProperty convention(@Nullable T value) { + onlyIfMutable(() -> super.convention(value)); + return this; + } + + @Override + public DefaultUnlockableProperty convention(Provider provider) { + onlyIfMutable(() -> super.convention(provider)); + return this; + } + } } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultDecompressionCacheFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultDecompressionCacheFactory.java new file mode 100644 index 000000000000..4070f4cdc269 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultDecompressionCacheFactory.java @@ -0,0 +1,61 @@ +/* + * 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.cache; + +import org.gradle.cache.internal.DecompressionCache; +import org.gradle.cache.internal.DecompressionCacheFactory; +import org.gradle.cache.internal.DefaultDecompressionCache; +import org.gradle.cache.scopes.ScopedCache; + +import javax.annotation.Nonnull; +import java.io.Closeable; +import java.io.IOException; + +/** + * Implements a factory to create a {@link DecompressionCache} for a given {@link ScopedCache}. + * + * This class manages a singleton cache and creates it on demand. Closing this factory will close the cache. + */ +public class DefaultDecompressionCacheFactory implements DecompressionCacheFactory, Closeable { + + private final ScopedCache scopedCache; + private volatile DecompressionCache cache; + + public DefaultDecompressionCacheFactory(ScopedCache scopedCache) { + this.scopedCache = scopedCache; + } + + @Nonnull + @Override + public DecompressionCache create() { + if (cache == null) { + synchronized (this) { + if (cache == null) { + cache = new DefaultDecompressionCache(scopedCache); + } + } + } + return cache; + } + + @Override + public void close() throws IOException { + if (cache != null) { + cache.close(); + } + } +} diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/DefaultFileOperations.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/DefaultFileOperations.java index 219d744c88be..ab0ec818892e 100755 --- a/subprojects/core/src/main/java/org/gradle/api/internal/file/DefaultFileOperations.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/DefaultFileOperations.java @@ -36,7 +36,6 @@ import org.gradle.api.internal.file.copy.FileCopier; import org.gradle.api.internal.file.delete.DefaultDeleteSpec; import org.gradle.api.internal.file.delete.DeleteSpecInternal; -import org.gradle.api.internal.file.temp.TemporaryFileProvider; import org.gradle.api.internal.provider.ProviderInternal; import org.gradle.api.internal.resources.ApiTextResourceAdapter; import org.gradle.api.internal.resources.DefaultResourceHandler; @@ -51,11 +50,11 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; +import org.gradle.cache.internal.DecompressionCacheFactory; import org.gradle.internal.Cast; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; -import org.gradle.internal.hash.StreamHasher; import org.gradle.internal.nativeintegration.filesystem.FileSystem; import org.gradle.internal.reflect.Instantiator; import org.gradle.internal.resource.local.LocalFileStandInExternalResource; @@ -74,11 +73,9 @@ @SuppressWarnings({"Convert2Lambda", "Anonymous2MethodRef"}) public class DefaultFileOperations implements FileOperations { private final FileResolver fileResolver; - private final TemporaryFileProvider temporaryFileProvider; private final Instantiator instantiator; private final Deleter deleter; private final ResourceHandler resourceHandler; - private final StreamHasher streamHasher; private final FileHasher fileHasher; private final Factory patternSetFactory; private final FileCopier fileCopier; @@ -87,13 +84,12 @@ public class DefaultFileOperations implements FileOperations { private final FileCollectionFactory fileCollectionFactory; private final TaskDependencyFactory taskDependencyFactory; private final ProviderFactory providers; + private final DecompressionCacheFactory decompressionCacheFactory; public DefaultFileOperations( FileResolver fileResolver, - TemporaryFileProvider temporaryFileProvider, Instantiator instantiator, DirectoryFileTreeFactory directoryFileTreeFactory, - StreamHasher streamHasher, FileHasher fileHasher, DefaultResourceHandler.Factory resourceHandlerFactory, FileCollectionFactory fileCollectionFactory, @@ -103,15 +99,14 @@ public DefaultFileOperations( Deleter deleter, DocumentationRegistry documentationRegistry, TaskDependencyFactory taskDependencyFactory, - ProviderFactory providers + ProviderFactory providers, + DecompressionCacheFactory decompressionCacheFactory ) { this.fileCollectionFactory = fileCollectionFactory; this.fileResolver = fileResolver; - this.temporaryFileProvider = temporaryFileProvider; this.instantiator = instantiator; this.directoryFileTreeFactory = directoryFileTreeFactory; this.resourceHandler = resourceHandlerFactory.create(this); - this.streamHasher = streamHasher; this.fileHasher = fileHasher; this.patternSetFactory = patternSetFactory; this.taskDependencyFactory = taskDependencyFactory; @@ -129,6 +124,7 @@ public DefaultFileOperations( ); this.fileSystem = fileSystem; this.deleter = deleter; + this.decompressionCacheFactory = decompressionCacheFactory; } @Override @@ -178,7 +174,7 @@ public ConfigurableFileTree fileTree(Map args) { @Override public FileTreeInternal zipTree(Object zipPath) { Provider fileProvider = asFileProvider(zipPath); - return new FileTreeAdapter(new ZipFileTree(fileProvider, getExpandDir(), fileSystem, directoryFileTreeFactory, fileHasher), taskDependencyFactory, patternSetFactory); + return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory.create()), taskDependencyFactory, patternSetFactory); } @Override @@ -193,7 +189,7 @@ public FileTreeInternal tarTree(Object tarPath) { } }); - TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), getExpandDir(), fileSystem, directoryFileTreeFactory, fileHasher); + TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory.create()); return new FileTreeAdapter(tarTree, taskDependencyFactory, patternSetFactory); } @@ -223,10 +219,6 @@ private Provider asFileProvider(Object path) { return providers.provider(() -> file(path)); } - private File getExpandDir() { - return temporaryFileProvider.newTemporaryFile("expandedArchives"); - } - @Override public String relativePath(Object path) { return fileResolver.resolveAsRelativePath(path); @@ -299,7 +291,6 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File ObjectFactory objectFactory = services.get(ObjectFactory.class); FileSystem fileSystem = services.get(FileSystem.class); DirectoryFileTreeFactory directoryFileTreeFactory = services.get(DirectoryFileTreeFactory.class); - StreamHasher streamHasher = services.get(StreamHasher.class); FileHasher fileHasher = services.get(FileHasher.class); ApiTextResourceAdapter.Factory textResourceAdapterFactory = services.get(ApiTextResourceAdapter.Factory.class); Factory patternSetFactory = services.getFactory(PatternSet.class); @@ -307,6 +298,7 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File DocumentationRegistry documentationRegistry = services.get(DocumentationRegistry.class); ProviderFactory providers = services.get(ProviderFactory.class); TaskDependencyFactory taskDependencyFactory = services.get(TaskDependencyFactory.class); + DecompressionCacheFactory decompressionCacheFactory = services.get(DecompressionCacheFactory.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, @@ -318,10 +310,8 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File return new DefaultFileOperations( fileResolver, - null, instantiator, directoryFileTreeFactory, - streamHasher, fileHasher, resourceHandlerFactory, fileTreeFactory, @@ -331,6 +321,7 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File deleter, documentationRegistry, taskDependencyFactory, - providers); + providers, + decompressionCacheFactory); } } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTree.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTree.java index 094a2b19a29a..9f9b34ed4708 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTree.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTree.java @@ -21,10 +21,22 @@ import org.gradle.api.internal.tasks.TaskDependencyContainer; import org.gradle.api.internal.tasks.TaskDependencyResolveContext; import org.gradle.api.provider.Provider; +import org.gradle.cache.internal.DecompressionCache; import java.io.File; -public abstract class AbstractArchiveFileTree implements FileSystemMirroringFileTree, TaskDependencyContainer { +/** + * Abstract base class for a {@link org.gradle.api.file.FileTree FileTree} that is backed by an archive file. + * + * Will decompress the archive file to the given cache. + */ +/* package */ abstract class AbstractArchiveFileTree implements FileSystemMirroringFileTree, TaskDependencyContainer { + protected final DecompressionCache decompressionCache; + + protected AbstractArchiveFileTree(DecompressionCache decompressionCache) { + this.decompressionCache = decompressionCache; + } + abstract protected Provider getBackingFileProvider(); private File getBackingFile() { diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeElement.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeElement.java new file mode 100644 index 000000000000..1691f562ac25 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeElement.java @@ -0,0 +1,103 @@ +/* + * 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.file.archive; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.file.RelativePath; +import org.gradle.api.internal.file.AbstractFileTreeElement; +import org.gradle.internal.file.Chmod; +import org.gradle.util.internal.GFileUtils; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An implementation of {@link org.gradle.api.file.FileTreeElement FileTreeElement} meant + * for use with archive files when subclassing {@link org.gradle.api.internal.file.AbstractFileTree AbstractFileTree}. + *

+ * This implementation extracts the files from the archive to the supplied expansion directory. + */ +public abstract class AbstractArchiveFileTreeElement extends AbstractFileTreeElement implements FileVisitDetails { + private final File expandedDir; + private File file; + private final AtomicBoolean stopFlag; + + /** + * Creates a new instance. + * + * @param chmod the chmod instance to use + * @param expandedDir the directory to extract the archived file to + * @param stopFlag the stop flag to use + */ + protected AbstractArchiveFileTreeElement(Chmod chmod, File expandedDir, AtomicBoolean stopFlag) { + super(chmod); + this.expandedDir = expandedDir; + this.stopFlag = stopFlag; + } + + /** + * Returns the archive entry for this element. + * + * @return the archive entry + * @implSpec this method should be overriden to return a more specific type + */ + protected abstract ArchiveEntry getArchiveEntry(); + + /** + * Returns a safe name for the name of a file contained in the archive. + * @see org.gradle.util.internal.ZipSlip#safeZipEntryName(String) + */ + protected abstract String safeEntryName(); + + @Override + public File getFile() { + if (file == null) { + file = new File(expandedDir, safeEntryName()); + if (!file.exists()) { + GFileUtils.mkdirs(file.getParentFile()); + copyTo(file); + } + } + return file; + } + + @Override + public RelativePath getRelativePath() { + return new RelativePath(!getArchiveEntry().isDirectory(), safeEntryName().split("/")); + } + + @Override + public long getLastModified() { + return getArchiveEntry().getLastModifiedDate().getTime(); + } + + @Override + public boolean isDirectory() { + return getArchiveEntry().isDirectory(); + } + + @Override + public long getSize() { + return getArchiveEntry().getSize(); + } + + @Override + public void stopVisiting() { + stopFlag.set(true); + } +} diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java index afc7ac309335..70b15446d5e4 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/TarFileTree.java @@ -20,15 +20,13 @@ import org.apache.commons.compress.utils.IOUtils; import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; -import org.gradle.api.file.FileVisitDetails; import org.gradle.api.file.FileVisitor; -import org.gradle.api.file.RelativePath; -import org.gradle.api.internal.file.AbstractFileTreeElement; import org.gradle.api.internal.file.collections.DirectoryFileTree; import org.gradle.api.internal.file.collections.DirectoryFileTreeFactory; import org.gradle.api.provider.Provider; import org.gradle.api.resources.ResourceException; import org.gradle.api.resources.internal.ReadableResourceInternal; +import org.gradle.cache.internal.DecompressionCache; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.hash.HashCode; @@ -47,15 +45,20 @@ public class TarFileTree extends AbstractArchiveFileTree { private final Provider resource; private final Chmod chmod; private final DirectoryFileTreeFactory directoryFileTreeFactory; - private final File tmpDir; private final FileHasher fileHasher; - public TarFileTree(Provider tarFileProvider, Provider resource, File tmpDir, Chmod chmod, DirectoryFileTreeFactory directoryFileTreeFactory, FileHasher fileHasher) { + public TarFileTree( + Provider tarFileProvider, + Provider resource, + Chmod chmod, + DirectoryFileTreeFactory directoryFileTreeFactory, + FileHasher fileHasher, + DecompressionCache decompressionCache) { + super(decompressionCache); this.tarFileProvider = tarFileProvider; this.resource = resource; this.chmod = chmod; this.directoryFileTreeFactory = directoryFileTreeFactory; - this.tmpDir = tmpDir; this.fileHasher = fileHasher; } @@ -71,34 +74,36 @@ public DirectoryFileTree getMirror() { @Override public void visit(FileVisitor visitor) { - InputStream inputStream; - try { - inputStream = new BufferedInputStream(resource.get().read()); - } catch (ResourceException e) { - throw cannotExpand(e); - } + decompressionCache.useCache(() -> { + InputStream inputStream; + try { + inputStream = new BufferedInputStream(resource.get().read()); + } catch (ResourceException e) { + throw cannotExpand(e); + } - try { try { - Objects.requireNonNull(visitor); - visitImpl(visitor, inputStream); - } finally { - inputStream.close(); + try { + Objects.requireNonNull(visitor); + visitImpl(visitor, inputStream); + } finally { + inputStream.close(); + } + } catch (Exception e) { + String message = "Unable to expand " + getDisplayName() + "\n" + + " The tar might be corrupted or it is compressed in an unexpected way.\n" + + " By default the tar tree tries to guess the compression based on the file extension.\n" + + " If you need to specify the compression explicitly please refer to the DSL reference."; + throw new GradleException(message, e); } - } catch (Exception e) { - String message = "Unable to expand " + getDisplayName() + "\n" - + " The tar might be corrupted or it is compressed in an unexpected way.\n" - + " By default the tar tree tries to guess the compression based on the file extension.\n" - + " If you need to specify the compression explicitly please refer to the DSL reference."; - throw new GradleException(message, e); - } + }); } private void visitImpl(FileVisitor visitor, InputStream inputStream) throws IOException { checkFormat(inputStream); AtomicBoolean stopFlag = new AtomicBoolean(); - NoCloseTarArchiveInputStream tar = new NoCloseTarArchiveInputStream(inputStream); + DetailsImpl.NoCloseTarArchiveInputStream tar = new DetailsImpl.NoCloseTarArchiveInputStream(inputStream); File expandedDir = getExpandedDir(); ReadableResourceInternal resource = this.resource.get(); TarArchiveEntry entry; @@ -119,8 +124,8 @@ public Provider getBackingFileProvider() { private File getExpandedDir() { File tarFile = tarFileProvider.get(); HashCode fileHash = hashFile(tarFile); - String expandedDirName = tarFile.getName() + "_" + fileHash; - return new File(tmpDir, expandedDirName); + String expandedDirName = "tar_" + fileHash; + return new File(decompressionCache.getBaseDir(), expandedDirName); } private HashCode hashFile(File tarFile) { @@ -135,93 +140,6 @@ private RuntimeException cannotExpand(Exception e) { throw new InvalidUserDataException(String.format("Cannot expand %s.", getDisplayName()), e); } - private static class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails { - private final TarArchiveEntry entry; - private final NoCloseTarArchiveInputStream tar; - private final AtomicBoolean stopFlag; - private final ReadableResourceInternal resource; - private final File expandedDir; - private File file; - private boolean read; - - public DetailsImpl(ReadableResourceInternal resource, File expandedDir, TarArchiveEntry entry, NoCloseTarArchiveInputStream tar, AtomicBoolean stopFlag, Chmod chmod) { - super(chmod); - this.resource = resource; - this.expandedDir = expandedDir; - this.entry = entry; - this.tar = tar; - this.stopFlag = stopFlag; - } - - @Override - public String getDisplayName() { - return String.format("tar entry %s!%s", resource.getDisplayName(), entry.getName()); - } - - @Override - public void stopVisiting() { - stopFlag.set(true); - } - - @Override - public File getFile() { - if (file == null) { - file = new File(expandedDir, entry.getName()); - if (!file.exists()) { - copyTo(file); - } - } - return file; - } - - @Override - public long getLastModified() { - return entry.getLastModifiedDate().getTime(); - } - - @Override - public boolean isDirectory() { - return entry.isDirectory(); - } - - @Override - public long getSize() { - return entry.getSize(); - } - - @Override - public InputStream open() { - if (read && file != null) { - return GFileUtils.openInputStream(file); - } - if (read || tar.getCurrentEntry() != entry) { - throw new UnsupportedOperationException(String.format("The contents of %s has already been read.", this)); - } - read = true; - return tar; - } - - @Override - public RelativePath getRelativePath() { - return new RelativePath(!entry.isDirectory(), entry.getName().split("/")); - } - - @Override - public int getMode() { - return entry.getMode() & 0777; - } - } - - private static class NoCloseTarArchiveInputStream extends TarArchiveInputStream { - public NoCloseTarArchiveInputStream(InputStream is) { - super(is); - } - - @Override - public void close() throws IOException { - } - } - /** * Using Apache Commons Compress to un-tar a non-tar archive fails silently, without any exception * or error, so we need a way of checking the format explicitly. @@ -259,4 +177,61 @@ private void checkFormat(InputStream inputStream) throws IOException { } throw new IOException("Not a TAR archive"); } + + private static final class DetailsImpl extends AbstractArchiveFileTreeElement { + private final TarArchiveEntry entry; + private final NoCloseTarArchiveInputStream tar; + private final ReadableResourceInternal resource; + private boolean read; + + public DetailsImpl(ReadableResourceInternal resource, File expandedDir, TarArchiveEntry entry, NoCloseTarArchiveInputStream tar, AtomicBoolean stopFlag, Chmod chmod) { + super(chmod, expandedDir, stopFlag); + this.resource = resource; + this.entry = entry; + this.tar = tar; + } + + @Override + public String getDisplayName() { + return String.format("tar entry %s!%s", resource.getDisplayName(), entry.getName()); + } + + @Override + public InputStream open() { + if (read) { + getFile(); + return GFileUtils.openInputStream(getFile()); + } else if (tar.getCurrentEntry() != entry) { + throw new UnsupportedOperationException(String.format("The contents of %s has already been read.", this)); + } else { + read = true; + return tar; + } + } + + @Override + public int getMode() { + return entry.getMode() & 0777; + } + + @Override + protected String safeEntryName() { + return entry.getName(); + } + + @Override + protected TarArchiveEntry getArchiveEntry() { + return entry; + } + + private static final class NoCloseTarArchiveInputStream extends TarArchiveInputStream { + public NoCloseTarArchiveInputStream(InputStream is) { + super(is); + } + + @Override + public void close() throws IOException { + } + } + } } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java index 2c9a2a1d97cc..8b1c6f559089 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipFileTree.java @@ -20,13 +20,11 @@ import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; import org.gradle.api.UncheckedIOException; -import org.gradle.api.file.FileVisitDetails; import org.gradle.api.file.FileVisitor; -import org.gradle.api.file.RelativePath; -import org.gradle.api.internal.file.AbstractFileTreeElement; import org.gradle.api.internal.file.collections.DirectoryFileTree; import org.gradle.api.internal.file.collections.DirectoryFileTreeFactory; import org.gradle.api.provider.Provider; +import org.gradle.cache.internal.DecompressionCache; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.nativeintegration.filesystem.FileSystem; @@ -45,20 +43,19 @@ public class ZipFileTree extends AbstractArchiveFileTree { private final Provider fileProvider; - private final File tmpDir; private final Chmod chmod; private final DirectoryFileTreeFactory directoryFileTreeFactory; private final FileHasher fileHasher; public ZipFileTree( Provider zipFile, - File tmpDir, Chmod chmod, DirectoryFileTreeFactory directoryFileTreeFactory, - FileHasher fileHasher + FileHasher fileHasher, + DecompressionCache decompressionCache ) { + super(decompressionCache); this.fileProvider = zipFile; - this.tmpDir = tmpDir; this.chmod = chmod; this.directoryFileTreeFactory = directoryFileTreeFactory; this.fileHasher = fileHasher; @@ -81,32 +78,34 @@ public DirectoryFileTree getMirror() { @Override public void visit(FileVisitor visitor) { - File zipFile = fileProvider.get(); - if (!zipFile.exists()) { - throw new InvalidUserDataException(format("Cannot expand %s as it does not exist.", getDisplayName())); - } - if (!zipFile.isFile()) { - throw new InvalidUserDataException(format("Cannot expand %s as it is not a file.", getDisplayName())); - } + decompressionCache.useCache(() -> { + File zipFile = fileProvider.get(); + if (!zipFile.exists()) { + throw new InvalidUserDataException(format("Cannot expand %s as it does not exist.", getDisplayName())); + } + if (!zipFile.isFile()) { + throw new InvalidUserDataException(format("Cannot expand %s as it is not a file.", getDisplayName())); + } - AtomicBoolean stopFlag = new AtomicBoolean(); - File expandedDir = getExpandedDir(); - try (ZipFile zip = new ZipFile(zipFile)) { - // The iteration order of zip.getEntries() is based on the hash of the zip entry. This isn't much use - // to us. So, collect the entries in a map and iterate over them in alphabetical order. - Iterator sortedEntries = entriesSortedByName(zip); - while (!stopFlag.get() && sortedEntries.hasNext()) { - ZipArchiveEntry entry = sortedEntries.next(); - DetailsImpl details = new DetailsImpl(zipFile, expandedDir, entry, zip, stopFlag, chmod); - if (entry.isDirectory()) { - visitor.visitDir(details); - } else { - visitor.visitFile(details); + AtomicBoolean stopFlag = new AtomicBoolean(); + File expandedDir = getExpandedDir(); + try (ZipFile zip = new ZipFile(zipFile)) { + // The iteration order of zip.getEntries() is based on the hash of the zip entry. This isn't much use + // to us. So, collect the entries in a map and iterate over them in alphabetical order. + Iterator sortedEntries = entriesSortedByName(zip); + while (!stopFlag.get() && sortedEntries.hasNext()) { + ZipArchiveEntry entry = sortedEntries.next(); + DetailsImpl details = new DetailsImpl(zipFile, expandedDir, entry, zip, stopFlag, chmod); + if (entry.isDirectory()) { + visitor.visitDir(details); + } else { + visitor.visitFile(details); + } } + } catch (Exception e) { + throw new GradleException(format("Cannot expand %s.", getDisplayName()), e); } - } catch (Exception e) { - throw new GradleException(format("Cannot expand %s.", getDisplayName()), e); - } + }); } private Iterator entriesSortedByName(ZipFile zip) { @@ -126,25 +125,20 @@ public Provider getBackingFileProvider() { private File getExpandedDir() { File zipFile = fileProvider.get(); - String expandedDirName = zipFile.getName() + "_" + fileHasher.hash(zipFile); - return new File(tmpDir, expandedDirName); + String expandedDirName = "zip_" + fileHasher.hash(zipFile); + return new File(decompressionCache.getBaseDir(), expandedDirName); } - private static class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails { + private static final class DetailsImpl extends AbstractArchiveFileTreeElement { private final File originalFile; - private final File expandedDir; private final ZipArchiveEntry entry; private final ZipFile zip; - private final AtomicBoolean stopFlag; - private File file; public DetailsImpl(File originalFile, File expandedDir, ZipArchiveEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod) { - super(chmod); + super(chmod, expandedDir, stopFlag); this.originalFile = originalFile; - this.expandedDir = expandedDir; this.entry = entry; this.zip = zip; - this.stopFlag = stopFlag; } @Override @@ -153,38 +147,13 @@ public String getDisplayName() { } @Override - public void stopVisiting() { - stopFlag.set(true); - } - - @Override - public File getFile() { - if (file == null) { - file = new File(expandedDir, safeEntryName()); - if (!file.exists()) { - copyTo(file); - } - } - return file; - } - - private String safeEntryName() { + protected String safeEntryName() { return safeZipEntryName(entry.getName()); } @Override - public long getLastModified() { - return entry.getTime(); - } - - @Override - public boolean isDirectory() { - return entry.isDirectory(); - } - - @Override - public long getSize() { - return entry.getSize(); + protected ZipArchiveEntry getArchiveEntry() { + return entry; } @Override @@ -196,11 +165,6 @@ public InputStream open() { } } - @Override - public RelativePath getRelativePath() { - return new RelativePath(!entry.isDirectory(), safeEntryName().split("/")); - } - @Override public int getMode() { int unixMode = entry.getUnixMode() & 0777; diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptClassPathResolver.java b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptClassPathResolver.java index ed060c081fe0..643de2fab729 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptClassPathResolver.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptClassPathResolver.java @@ -15,20 +15,55 @@ */ package org.gradle.api.internal.initialization; +import org.gradle.api.JavaVersion; import org.gradle.api.artifacts.ArtifactView; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.attributes.AttributeContainer; +import org.gradle.api.attributes.Bundling; +import org.gradle.api.attributes.Category; +import org.gradle.api.attributes.LibraryElements; +import org.gradle.api.attributes.Usage; +import org.gradle.api.attributes.java.TargetJvmVersion; +import org.gradle.api.attributes.plugin.GradlePluginApiVersion; import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactoryInternal; +import org.gradle.api.internal.model.NamedObjectInstantiator; +import org.gradle.internal.classpath.CachedClasspathTransformer; import org.gradle.internal.classpath.ClassPath; import org.gradle.internal.classpath.DefaultClassPath; import org.gradle.internal.component.local.model.OpaqueComponentIdentifier; +import org.gradle.internal.logging.util.Log4jBannedVersion; +import org.gradle.util.GradleVersion; import java.util.List; public class DefaultScriptClassPathResolver implements ScriptClassPathResolver { private final List initializers; + private final NamedObjectInstantiator instantiator; + private final CachedClasspathTransformer classpathTransformer; - public DefaultScriptClassPathResolver(List initializers) { + public DefaultScriptClassPathResolver(List initializers, NamedObjectInstantiator instantiator, CachedClasspathTransformer classpathTransformer) { this.initializers = initializers; + this.instantiator = instantiator; + this.classpathTransformer = classpathTransformer; + } + + @Override + public void prepareClassPath(Configuration configuration, DependencyHandler dependencyHandler) { + // should ideally reuse the `JvmEcosystemUtilities` but this code is too low level + // and this service is therefore not available! + AttributeContainer attributes = configuration.getAttributes(); + attributes.attribute(Usage.USAGE_ATTRIBUTE, instantiator.named(Usage.class, Usage.JAVA_RUNTIME)); + attributes.attribute(Category.CATEGORY_ATTRIBUTE, instantiator.named(Category.class, Category.LIBRARY)); + attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, instantiator.named(LibraryElements.class, LibraryElements.JAR)); + attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, instantiator.named(Bundling.class, Bundling.EXTERNAL)); + attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.parseInt(JavaVersion.current().getMajorVersion())); + attributes.attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, instantiator.named(GradlePluginApiVersion.class, GradleVersion.current().getVersion())); + + configuration.getDependencyConstraints().add(dependencyHandler.getConstraints().create(Log4jBannedVersion.LOG4J2_CORE_COORDINATES, constraint -> constraint.version(version -> { + version.require(Log4jBannedVersion.LOG4J2_CORE_REQUIRED_VERSION); + version.reject(Log4jBannedVersion.LOG4J2_CORE_VULNERABLE_VERSION_RANGE); + }))); } @Override @@ -48,6 +83,6 @@ public ClassPath resolveClassPath(Configuration classpathConfiguration) { return true; }); }); - return DefaultClassPath.of(view.getFiles()); + return classpathTransformer.transform(DefaultClassPath.of(view.getFiles()), CachedClasspathTransformer.StandardTransform.BuildLogic); } } 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 01f8bc0c71ad..094da7f6669d 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 @@ -16,35 +16,24 @@ package org.gradle.api.internal.initialization; import groovy.lang.Closure; -import org.gradle.api.JavaVersion; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.artifacts.dsl.DependencyLockingHandler; import org.gradle.api.artifacts.dsl.RepositoryHandler; -import org.gradle.api.attributes.AttributeContainer; -import org.gradle.api.attributes.Bundling; -import org.gradle.api.attributes.Category; -import org.gradle.api.attributes.LibraryElements; -import org.gradle.api.attributes.Usage; -import org.gradle.api.attributes.java.TargetJvmVersion; -import org.gradle.api.attributes.plugin.GradlePluginApiVersion; import org.gradle.api.initialization.dsl.ScriptHandler; 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.model.NamedObjectInstantiator; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; import org.gradle.groovy.scripts.ScriptSource; import org.gradle.internal.classloader.ClasspathUtil; import org.gradle.internal.classpath.ClassPath; -import org.gradle.internal.logging.util.Log4jBannedVersion; import org.gradle.internal.metaobject.BeanDynamicObject; import org.gradle.internal.metaobject.DynamicObject; import org.gradle.internal.resource.ResourceLocation; import org.gradle.util.internal.ConfigureUtil; -import org.gradle.util.GradleVersion; import java.io.File; import java.net.URI; @@ -56,7 +45,6 @@ public class DefaultScriptHandler implements ScriptHandler, ScriptHandlerInterna private final ClassLoaderScope classLoaderScope; private final ScriptClassPathResolver scriptClassPathResolver; private final DependencyResolutionServices dependencyResolutionServices; - private final NamedObjectInstantiator instantiator; private final DependencyLockingHandler dependencyLockingHandler; // The following values are relatively expensive to create, so defer creation until required private RepositoryHandler repositoryHandler; @@ -65,13 +53,11 @@ public class DefaultScriptHandler implements ScriptHandler, ScriptHandlerInterna private Configuration classpathConfiguration; private DynamicObject dynamicObject; - public DefaultScriptHandler(ScriptSource scriptSource, DependencyResolutionServices dependencyResolutionServices, ClassLoaderScope classLoaderScope, - ScriptClassPathResolver scriptClassPathResolver, NamedObjectInstantiator instantiator) { + public DefaultScriptHandler(ScriptSource scriptSource, DependencyResolutionServices dependencyResolutionServices, ClassLoaderScope classLoaderScope, ScriptClassPathResolver scriptClassPathResolver) { this.dependencyResolutionServices = dependencyResolutionServices; this.scriptResource = scriptSource.getResource().getLocation(); this.classLoaderScope = classLoaderScope; this.scriptClassPathResolver = scriptClassPathResolver; - this.instantiator = instantiator; this.dependencyLockingHandler = dependencyResolutionServices.getDependencyLockingHandler(); JavaEcosystemSupport.configureSchema(dependencyResolutionServices.getAttributesSchema(), dependencyResolutionServices.getObjectFactory()); } @@ -92,7 +78,7 @@ public ClassPath getScriptClassPath() { } @Override - public ClassPath getNonInstrumentedScriptClassPath() { + public ClassPath getInstrumentedScriptClassPath() { return scriptClassPathResolver.resolveClassPath(classpathConfiguration); } @@ -131,20 +117,7 @@ private void defineConfiguration() { } if (classpathConfiguration == null) { classpathConfiguration = configContainer.create(CLASSPATH_CONFIGURATION); - // should ideally reuse the `JvmEcosystemUtilities` but this code is too low level - // and this service is therefore not available! - AttributeContainer attributes = classpathConfiguration.getAttributes(); - attributes.attribute(Usage.USAGE_ATTRIBUTE, instantiator.named(Usage.class, Usage.JAVA_RUNTIME)); - attributes.attribute(Category.CATEGORY_ATTRIBUTE, instantiator.named(Category.class, Category.LIBRARY)); - attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, instantiator.named(LibraryElements.class, LibraryElements.JAR)); - attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, instantiator.named(Bundling.class, Bundling.EXTERNAL)); - attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.parseInt(JavaVersion.current().getMajorVersion())); - attributes.attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, instantiator.named(GradlePluginApiVersion.class, GradleVersion.current().getVersion())); - - classpathConfiguration.getDependencyConstraints().add(dependencyHandler.getConstraints().create(Log4jBannedVersion.LOG4J2_CORE_COORDINATES, constraint -> constraint.version(version -> { - version.require(Log4jBannedVersion.LOG4J2_CORE_REQUIRED_VERSION); - version.reject(Log4jBannedVersion.LOG4J2_CORE_VULNERABLE_VERSION_RANGE); - }))); + scriptClassPathResolver.prepareClassPath(classpathConfiguration, dependencyHandler); } } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java index 1fa52130623b..064cbb5a0ae4 100755 --- a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java @@ -24,7 +24,6 @@ import org.gradle.api.internal.artifacts.dsl.dependencies.UnknownProjectFinder; import org.gradle.api.internal.file.FileCollectionFactory; import org.gradle.api.internal.file.FileResolver; -import org.gradle.api.internal.model.NamedObjectInstantiator; import org.gradle.groovy.scripts.ScriptSource; public class DefaultScriptHandlerFactory implements ScriptHandlerFactory { @@ -32,7 +31,6 @@ public class DefaultScriptHandlerFactory implements ScriptHandlerFactory { private final FileCollectionFactory fileCollectionFactory; private final DependencyMetaDataProvider dependencyMetaDataProvider; private final ScriptClassPathResolver scriptClassPathResolver; - private final NamedObjectInstantiator instantiator; private final FileResolver fileResolver; private final ProjectFinder projectFinder = new UnknownProjectFinder("Cannot use project dependencies in a script classpath definition."); @@ -40,14 +38,12 @@ public DefaultScriptHandlerFactory(DependencyManagementServices dependencyManage FileResolver fileResolver, FileCollectionFactory fileCollectionFactory, DependencyMetaDataProvider dependencyMetaDataProvider, - ScriptClassPathResolver scriptClassPathResolver, - NamedObjectInstantiator instantiator) { + ScriptClassPathResolver scriptClassPathResolver) { this.dependencyManagementServices = dependencyManagementServices; this.fileResolver = fileResolver; this.fileCollectionFactory = fileCollectionFactory; this.dependencyMetaDataProvider = dependencyMetaDataProvider; this.scriptClassPathResolver = scriptClassPathResolver; - this.instantiator = instantiator; } @Override @@ -58,7 +54,6 @@ public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoaderScope @Override public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoaderScope classLoaderScope, DomainObjectContext context) { DependencyResolutionServices services = dependencyManagementServices.create(fileResolver, fileCollectionFactory, dependencyMetaDataProvider, projectFinder, context); - return new DefaultScriptHandler(scriptSource, services, classLoaderScope, scriptClassPathResolver, instantiator); + return new DefaultScriptHandler(scriptSource, services, classLoaderScope, scriptClassPathResolver); } - } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptClassPathResolver.java b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptClassPathResolver.java index 91fb3a4c8980..77574b15a32d 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptClassPathResolver.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptClassPathResolver.java @@ -16,12 +16,21 @@ package org.gradle.api.internal.initialization; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.internal.classpath.ClassPath; +import org.gradle.internal.service.scopes.Scopes; +import org.gradle.internal.service.scopes.ServiceScope; /** * Resolves a build script classpath to a set of files in a composite build, ensuring that the * required tasks are executed to build artifacts in included builds. */ +@ServiceScope(Scopes.Build.class) public interface ScriptClassPathResolver { + /** + * Prepares the given configuration for script classpath resolution, setting the relevant attributes and other metadata. + */ + void prepareClassPath(Configuration configuration, DependencyHandler dependencyHandler); + ClassPath resolveClassPath(Configuration classpath); } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptHandlerInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptHandlerInternal.java index 1f4d14e40ffa..11afec1368bd 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptHandlerInternal.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ScriptHandlerInternal.java @@ -31,5 +31,5 @@ public interface ScriptHandlerInternal extends ScriptHandler { /** * @return The resolved non-instrumented script classpath. */ - ClassPath getNonInstrumentedScriptClassPath(); + ClassPath getInstrumentedScriptClassPath(); } 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 492880d01069..0073a94c1b1b 100644 --- a/subprojects/core/src/main/java/org/gradle/configuration/BuildTreePreparingProjectsPreparer.java +++ b/subprojects/core/src/main/java/org/gradle/configuration/BuildTreePreparingProjectsPreparer.java @@ -54,14 +54,16 @@ public void prepareProjects(GradleInternal gradle) { generateDependenciesAccessorsAndAssignPluginVersions(gradle.getServices(), settings, buildSrcClassLoaderScope); // attaches root project buildLoader.load(gradle.getSettings(), gradle); - // Makes included build substitutions available - if (gradle.isRootBuild()) { - coordinator.registerGlobalLibrarySubstitutions(); - } + + // Makes included build substitutions available for this build + coordinator.registerSubstitutionsAvailableFor(gradle.getOwner()); + // Build buildSrc and export classpath to root project buildBuildSrcAndLockClassloader(gradle, buildSrcClassLoaderScope); delegate.prepareProjects(gradle); + + coordinator.registerSubstitutionsProvidedBy(gradle.getOwner()); } private void buildBuildSrcAndLockClassloader(GradleInternal gradle, ClassLoaderScope baseProjectClassLoaderScope) { diff --git a/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java b/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java index 7dc5f0af1f7a..38930171bea0 100644 --- a/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java +++ b/subprojects/core/src/main/java/org/gradle/configuration/DefaultProjectsPreparer.java @@ -17,9 +17,7 @@ import org.gradle.api.internal.GradleInternal; import org.gradle.execution.ProjectConfigurer; -import org.gradle.initialization.ModelConfigurationListener; import org.gradle.initialization.ProjectsEvaluatedNotifier; -import org.gradle.internal.build.BuildStateRegistry; import org.gradle.internal.buildtree.BuildModelParameters; import org.gradle.internal.operations.BuildOperationExecutor; @@ -27,21 +25,15 @@ public class DefaultProjectsPreparer implements ProjectsPreparer { private final BuildOperationExecutor buildOperationExecutor; private final ProjectConfigurer projectConfigurer; private final BuildModelParameters buildModelParameters; - private final ModelConfigurationListener modelConfigurationListener; - private final BuildStateRegistry buildStateRegistry; public DefaultProjectsPreparer( ProjectConfigurer projectConfigurer, BuildModelParameters buildModelParameters, - ModelConfigurationListener modelConfigurationListener, - BuildOperationExecutor buildOperationExecutor, - BuildStateRegistry buildStateRegistry + BuildOperationExecutor buildOperationExecutor ) { this.projectConfigurer = projectConfigurer; this.buildModelParameters = buildModelParameters; - this.modelConfigurationListener = modelConfigurationListener; this.buildOperationExecutor = buildOperationExecutor; - this.buildStateRegistry = buildStateRegistry; } @Override @@ -50,12 +42,5 @@ public void prepareProjects(GradleInternal gradle) { projectConfigurer.configureHierarchy(gradle.getRootProject()); new ProjectsEvaluatedNotifier(buildOperationExecutor).notify(gradle); } - - if (gradle.isRootBuild()) { - // Make root build substitutions available - buildStateRegistry.afterConfigureRootBuild(); - } - - modelConfigurationListener.onConfigure(gradle); } } diff --git a/subprojects/core/src/main/java/org/gradle/execution/selection/DefaultBuildTaskSelector.java b/subprojects/core/src/main/java/org/gradle/execution/selection/DefaultBuildTaskSelector.java index 4c2cf15f1c89..891864432637 100644 --- a/subprojects/core/src/main/java/org/gradle/execution/selection/DefaultBuildTaskSelector.java +++ b/subprojects/core/src/main/java/org/gradle/execution/selection/DefaultBuildTaskSelector.java @@ -40,6 +40,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Pattern; public class DefaultBuildTaskSelector implements BuildTaskSelector { @@ -103,12 +104,12 @@ public BuildSpecificSelector relativeToBuild(BuildState target) { } private ProjectResolutionResult resolveProject(TaskSelector.SelectionContext context, Path path, BuildState targetBuild) { - // Just a name -> use default project + select all + // Just a name -> use default project + select tasks with matching name in default project and subprojects if (!path.isAbsolute() && path.segmentCount() == 1) { return new ProjectResolutionResult(targetBuild, targetBuild.getMutableModel().getDefaultProject().getOwner(), true, path.getName()); } - // :name -> resolve + select exact + // :name -> resolve to a project + select task with matching name in that project // when is absolute -> resolve relative to root project // when is relative -> resolve relative to default project @@ -134,11 +135,16 @@ private ProjectResolutionResult resolveProject(TaskSelector.SelectionContext con private ProjectState selectProject(TaskSelector.SelectionContext context, ProjectState project, String childName) { Map candidates = new LinkedHashMap<>(); - if (project.getIdentityPath().equals(Path.ROOT)) { - for (IncludedBuildState build : buildRegistry.getIncludedBuilds()) { - ProjectState rootProject = build.getProjects().getRootProject(); - candidates.put(rootProject.getIdentityPath().getName(), rootProject); - } + if (project.getProjectPath().equals(Path.ROOT)) { + // Project is the root of a build, so include the root projects of any builds nested under that build + buildRegistry.visitBuilds(build -> { + if (build.isImportableBuild() && build.isProjectsLoaded()) { + ProjectState rootProject = build.getProjects().getRootProject(); + if (Objects.equals(rootProject.getIdentityPath().getParent(), project.getIdentityPath())) { + candidates.put(rootProject.getIdentityPath().getName(), rootProject); + } + } + }); } for (ProjectState child : project.getChildProjects()) { ProjectState previous = candidates.put(child.getIdentityPath().getName(), child); diff --git a/subprojects/core/src/main/java/org/gradle/initialization/DefaultSettingsLoaderFactory.java b/subprojects/core/src/main/java/org/gradle/initialization/DefaultSettingsLoaderFactory.java index 33409a4f1ea7..15589afb133f 100644 --- a/subprojects/core/src/main/java/org/gradle/initialization/DefaultSettingsLoaderFactory.java +++ b/subprojects/core/src/main/java/org/gradle/initialization/DefaultSettingsLoaderFactory.java @@ -66,7 +66,6 @@ public SettingsLoader forTopLevelBuild() { new CommandLineIncludedBuildSettingsLoader( defaultSettingsLoader() ), - buildRegistry, buildIncluder), buildRegistry), initScriptHandler), @@ -81,7 +80,6 @@ public SettingsLoader forNestedBuild() { new InitScriptHandlingSettingsLoader( new ChildBuildRegisteringSettingsLoader( defaultSettingsLoader(), - buildRegistry, buildIncluder), initScriptHandler), buildLayoutFactory, diff --git a/subprojects/core/src/main/java/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java b/subprojects/core/src/main/java/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java index 7e05f7ea9de9..8fa329e264df 100644 --- a/subprojects/core/src/main/java/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java +++ b/subprojects/core/src/main/java/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java @@ -63,8 +63,7 @@ public SettingsState process( SettingsState state = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(), settingsScript, gradleProperties, startParameter, baseClassLoaderScope); SettingsInternal settings = state.getSettings(); - gradle.getBuildListenerBroadcaster().beforeSettings(settings); - settings.getCaches().finalizeConfigurations(); + settings.getCaches().withMutableValues(() -> gradle.getBuildListenerBroadcaster().beforeSettings(settings)); applySettingsScript(settingsScript, settings); LOGGER.debug("Timing: Processing settings took: {}", settingsProcessingClock.getElapsed()); return state; diff --git a/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSourceBuilder.java b/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSourceBuilder.java index e463d9887aa2..675d32b9bc4a 100644 --- a/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSourceBuilder.java +++ b/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSourceBuilder.java @@ -24,7 +24,6 @@ import org.gradle.internal.build.BuildStateRegistry; import org.gradle.internal.build.PublicBuildPath; import org.gradle.internal.build.StandAloneNestedBuild; -import org.gradle.internal.classpath.CachedClasspathTransformer; import org.gradle.internal.classpath.ClassPath; import org.gradle.internal.operations.BuildOperationContext; import org.gradle.internal.operations.BuildOperationDescriptor; @@ -45,26 +44,20 @@ public class BuildSourceBuilder { private final BuildState currentBuild; private final FileLockManager fileLockManager; private final BuildOperationExecutor buildOperationExecutor; - private final CachedClasspathTransformer cachedClasspathTransformer; private final BuildSrcBuildListenerFactory buildSrcBuildListenerFactory; private final BuildStateRegistry buildRegistry; private final PublicBuildPath publicBuildPath; - public BuildSourceBuilder(BuildState currentBuild, FileLockManager fileLockManager, BuildOperationExecutor buildOperationExecutor, CachedClasspathTransformer cachedClasspathTransformer, BuildSrcBuildListenerFactory buildSrcBuildListenerFactory, BuildStateRegistry buildRegistry, PublicBuildPath publicBuildPath) { + public BuildSourceBuilder(BuildState currentBuild, FileLockManager fileLockManager, BuildOperationExecutor buildOperationExecutor, BuildSrcBuildListenerFactory buildSrcBuildListenerFactory, BuildStateRegistry buildRegistry, PublicBuildPath publicBuildPath) { this.currentBuild = currentBuild; this.fileLockManager = fileLockManager; this.buildOperationExecutor = buildOperationExecutor; - this.cachedClasspathTransformer = cachedClasspathTransformer; this.buildSrcBuildListenerFactory = buildSrcBuildListenerFactory; this.buildRegistry = buildRegistry; this.publicBuildPath = publicBuildPath; } public ClassPath buildAndGetClassPath(GradleInternal gradle) { - return createBuildSourceClasspath(); - } - - private ClassPath createBuildSourceClasspath() { StandAloneNestedBuild buildSrcBuild = buildRegistry.getBuildSrcNestedBuild(currentBuild); if (buildSrcBuild == null) { return ClassPath.EMPTY; @@ -99,7 +92,7 @@ public String getBuildPath() { private ClassPath buildBuildSrc(StandAloneNestedBuild buildSrcBuild) { return buildSrcBuild.run(buildController -> { try (FileLock ignored = buildSrcBuildLockFor(buildSrcBuild)) { - return new BuildSrcUpdateFactory(buildController, buildSrcBuildListenerFactory, cachedClasspathTransformer).create(); + return new BuildSrcUpdateFactory(buildSrcBuildListenerFactory).create(buildController); } }); } 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 ee5ee7c1e6e3..9f671919cbbb 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 @@ -17,68 +17,79 @@ package org.gradle.initialization.buildsrc; import org.gradle.api.Action; -import org.gradle.api.file.FileCollection; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.internal.GradleInternal; -import org.gradle.api.internal.component.BuildableJavaComponent; -import org.gradle.api.internal.component.ComponentRegistry; +import org.gradle.api.internal.initialization.DefaultScriptClassPathResolver; +import org.gradle.api.internal.initialization.ScriptClassPathResolver; +import org.gradle.api.internal.model.NamedObjectInstantiator; import org.gradle.api.internal.project.ProjectInternal; import org.gradle.api.internal.project.ProjectState; +import org.gradle.api.internal.tasks.TaskDependencyUtil; import org.gradle.api.invocation.Gradle; -import org.gradle.initialization.ModelConfigurationListener; -import org.gradle.internal.Actions; +import org.gradle.execution.EntryTaskSelector; +import org.gradle.execution.plan.ExecutionPlan; import org.gradle.internal.InternalBuildAdapter; +import org.gradle.internal.classpath.CachedClasspathTransformer; +import org.gradle.internal.classpath.ClassPath; +import org.gradle.internal.service.scopes.Scopes; +import org.gradle.internal.service.scopes.ServiceScope; -import java.io.File; -import java.util.Collection; +import java.util.Collections; +@ServiceScope(Scopes.Build.class) public class BuildSrcBuildListenerFactory { - private final Action buildSrcRootProjectConfiguration; + private final NamedObjectInstantiator instantiator; + private final CachedClasspathTransformer classpathTransformer; - public BuildSrcBuildListenerFactory() { - this(Actions.doNothing()); - } - - public BuildSrcBuildListenerFactory(Action buildSrcRootProjectConfiguration) { + public BuildSrcBuildListenerFactory(Action buildSrcRootProjectConfiguration, NamedObjectInstantiator instantiator, CachedClasspathTransformer classpathTransformer) { this.buildSrcRootProjectConfiguration = buildSrcRootProjectConfiguration; + this.instantiator = instantiator; + this.classpathTransformer = classpathTransformer; } Listener create() { - return new Listener(buildSrcRootProjectConfiguration); + return new Listener(buildSrcRootProjectConfiguration, instantiator, classpathTransformer); } /** * Inspects the build when configured, and adds the appropriate task to build the "main" `buildSrc` component. * On build completion, makes the runtime classpath of the main `buildSrc` component available. */ - public static class Listener extends InternalBuildAdapter implements ModelConfigurationListener { - private FileCollection classpath; + public static class Listener extends InternalBuildAdapter implements EntryTaskSelector { + private Configuration classpathConfiguration; private ProjectState rootProjectState; private final Action rootProjectConfiguration; + private final ScriptClassPathResolver resolver; - private Listener(Action rootProjectConfiguration) { + private Listener(Action rootProjectConfiguration, NamedObjectInstantiator instantiator, CachedClasspathTransformer classpathTransformer) { this.rootProjectConfiguration = rootProjectConfiguration; + this.resolver = new DefaultScriptClassPathResolver(Collections.emptyList(), instantiator, classpathTransformer); } @Override public void projectsLoaded(Gradle gradle) { - rootProjectConfiguration.execute((ProjectInternal) gradle.getRootProject()); + GradleInternal gradleInternal = (GradleInternal) gradle; + // Run only those tasks scheduled by this selector and not the default tasks + gradleInternal.getStartParameter().setTaskRequests(Collections.emptyList()); + ProjectInternal rootProject = gradleInternal.getRootProject(); + rootProjectState = rootProject.getOwner(); + rootProjectConfiguration.execute(rootProject); } @Override - public void onConfigure(GradleInternal gradle) { - final BuildableJavaComponent mainComponent = mainComponentOf(gradle); - gradle.getStartParameter().setTaskNames(mainComponent.getBuildTasks()); - classpath = mainComponent.getRuntimeClasspath(); - rootProjectState = gradle.getRootProject().getOwner(); - } - - public Collection getRuntimeClasspath() { - return rootProjectState.fromMutableState(p -> classpath.getFiles()); + public void applyTasksTo(Context context, ExecutionPlan plan) { + rootProjectState.applyToMutableState(rootProject -> { + classpathConfiguration = rootProject.getConfigurations().create("buildScriptClasspath"); + classpathConfiguration.setCanBeConsumed(false); + resolver.prepareClassPath(classpathConfiguration, rootProject.getDependencies()); + classpathConfiguration.getDependencies().add(rootProject.getDependencies().create(rootProject)); + plan.addEntryTasks(TaskDependencyUtil.getDependenciesForInternalUse(classpathConfiguration.getBuildDependencies(), null)); + }); } - private BuildableJavaComponent mainComponentOf(GradleInternal gradle) { - return gradle.getRootProject().getServices().get(ComponentRegistry.class).getMainComponent(); + public ClassPath getRuntimeClasspath() { + return rootProjectState.fromMutableState(project -> resolver.resolveClassPath(classpathConfiguration)); } } } diff --git a/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java b/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java index 7f267b7f1433..cc4a9e8fd3f5 100644 --- a/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java +++ b/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcUpdateFactory.java @@ -16,44 +16,24 @@ package org.gradle.initialization.buildsrc; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; import org.gradle.internal.buildtree.BuildTreeLifecycleController; -import org.gradle.internal.classpath.CachedClasspathTransformer; import org.gradle.internal.classpath.ClassPath; -import org.gradle.internal.classpath.DefaultClassPath; import javax.annotation.Nonnull; -import java.io.File; -import java.util.Collection; - -import static org.gradle.internal.classpath.CachedClasspathTransformer.StandardTransform.BuildLogic; public class BuildSrcUpdateFactory { - private static final Logger LOGGER = Logging.getLogger(BuildSrcUpdateFactory.class); - - private final BuildTreeLifecycleController buildController; private final BuildSrcBuildListenerFactory listenerFactory; - private final CachedClasspathTransformer cachedClasspathTransformer; - public BuildSrcUpdateFactory(BuildTreeLifecycleController buildController, BuildSrcBuildListenerFactory listenerFactory, CachedClasspathTransformer cachedClasspathTransformer) { - this.buildController = buildController; + public BuildSrcUpdateFactory(BuildSrcBuildListenerFactory listenerFactory) { this.listenerFactory = listenerFactory; - this.cachedClasspathTransformer = cachedClasspathTransformer; } @Nonnull - public ClassPath create() { - Collection classpath = build(); - LOGGER.debug("Gradle source classpath is: {}", classpath); - return cachedClasspathTransformer.transform(DefaultClassPath.of(classpath), BuildLogic); - } - - private Collection build() { + public ClassPath create(BuildTreeLifecycleController buildController) { BuildSrcBuildListenerFactory.Listener listener = listenerFactory.create(); buildController.beforeBuild(gradle -> gradle.addListener(listener)); - buildController.scheduleAndRunTasks(); + buildController.scheduleAndRunTasks(listener); return listener.getRuntimeClasspath(); } diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildIncluder.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildIncluder.java index 2900cddb7a0d..30a0f5a0cbfc 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildIncluder.java +++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildIncluder.java @@ -27,9 +27,29 @@ */ @ServiceScope(Scopes.Build.class) public interface BuildIncluder { - IncludedBuildState includeBuild(IncludedBuildSpec includedBuildSpec); + /** + * Registers an included build of the current build. An included build may provide plugins and libraries to this build and all other builds in the tree. + */ + CompositeBuildParticipantBuildState includeBuild(IncludedBuildSpec includedBuildSpec); + /** + * Registers an included plugin build of the current build. An included plugin build may provide plugins to this build only. In contrast to {@link #includeBuild(IncludedBuildSpec)}, + * this method does not make any libraries visible to this build, nor does it make anything visible to other builds in the tree. + */ void registerPluginBuild(IncludedBuildSpec includedBuildSpec); - Collection includeRegisteredPluginBuilds(); + /** + * Returns the set of plugin builds for this build. These are registered using {@link #registerPluginBuild(IncludedBuildSpec)}. + */ + Collection getRegisteredPluginBuilds(); + + /** + * Returns the set of included builds that are visible to this build for plugin resolution. + */ + Collection getIncludedBuildsForPluginResolution(); + + /** + * Prepares an included build so that the plugins it provides can be resolved. + */ + void prepareForPluginResolution(IncludedBuildState build); } 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 201310cd46b4..0916df730d0c 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 @@ -95,7 +95,7 @@ public interface BuildState { File getBuildRootDir(); /** - * Returns the current state of the mutable model of this build. + * Returns the current state of the mutable model of this build. Try to use {@link #withState(Transformer)} instead. */ GradleInternal getMutableModel(); diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildStateRegistry.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildStateRegistry.java index 7453d51c6c93..411957dfb626 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildStateRegistry.java +++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildStateRegistry.java @@ -49,7 +49,7 @@ public interface BuildStateRegistry { RootBuildState getRootBuild() throws IllegalStateException; /** - * Returns all children of the root build. + * Returns all included builds. */ Collection getIncludedBuilds(); @@ -66,15 +66,10 @@ public interface BuildStateRegistry { /** * Notification that the settings have been loaded for the root build. * - * This shouldn't be on this interface, as this is state for the root build that should be managed internally by the {@link RootBuildState} instance instead. This method is here to allow transition towards that structure. + *

This shouldn't be on this interface, as this is state for the root build that should be managed internally by the {@link RootBuildState} instance instead. This method is here to allow transition towards that structure. */ void finalizeIncludedBuilds(); - /** - * Notification that the root build has just finished configuration. - */ - void afterConfigureRootBuild(); - /** * Creates an included build. An included build is-a nested build whose projects and outputs are treated as part of the composite build. */ @@ -101,21 +96,6 @@ public interface BuildStateRegistry { */ void visitBuilds(Consumer visitor); - /** - * Register dependency substitutions for the given build. - */ - void registerSubstitutionsFor(IncludedBuildState build); - - /** - * Register dependency substitutions for the root build itself. This way, the projects of the root build can be addressed by coordinates as the projects of all other builds. - */ - void registerSubstitutionsForRootBuild(); - - /** - * Ensures that this project and any builds it includes are configured and their publications are registered. - */ - void ensureConfigured(IncludedBuildState buildState); - /** * Restarts each build in the tree. */ 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 9a190cdcb907..5e2ea4239393 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 @@ -25,6 +25,7 @@ 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; import org.gradle.internal.featurelifecycle.LoggingDeprecatedFeatureHandler; import org.gradle.internal.featurelifecycle.ScriptUsageLocationReporter; @@ -76,18 +77,12 @@ public BuildLifecycleController newInstance(BuildDefinition buildDefinition, Bui @SuppressWarnings("deprecation") File customSettingsFile = startParameter.getSettingsFile(); if (customSettingsFile != null) { - DeprecationLogger.deprecateAction("Specifying custom settings file location") - .willBeRemovedInGradle8() - .withUpgradeGuideSection(7, "configuring_custom_build_layout") - .nagUser(); + logFileDeprecationWarning(DeprecationLogger.deprecateAction("Specifying custom settings file location")); } @SuppressWarnings("deprecation") File customBuildFile = startParameter.getBuildFile(); if (customBuildFile != null) { - DeprecationLogger.deprecateAction("Specifying custom build file location") - .willBeRemovedInGradle8() - .withUpgradeGuideSection(7, "configuring_custom_build_layout") - .nagUser(); + logFileDeprecationWarning(DeprecationLogger.deprecateAction("Specifying custom build file location")); } GradleInternal gradle = buildScopeServices.get(GradleInternal.class); @@ -106,4 +101,11 @@ public BuildLifecycleController newInstance(BuildDefinition buildDefinition, Bui stateTransitionControllerFactory ); } + + private static void logFileDeprecationWarning(DeprecationMessageBuilder specifyingCustomfileLocation) { + specifyingCustomfileLocation + .willBeRemovedInGradle9() + .withUpgradeGuideSection(7, "configuring_custom_build_layout") + .nagUser(); + } } diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/StandAloneNestedBuild.java b/subprojects/core/src/main/java/org/gradle/internal/build/StandAloneNestedBuild.java index c85c5af2e9e3..6bd5dc8b8443 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/build/StandAloneNestedBuild.java +++ b/subprojects/core/src/main/java/org/gradle/internal/build/StandAloneNestedBuild.java @@ -20,4 +20,5 @@ * A stand alone nested build, which is a nested build that runs as part of some containing build as a single atomic step, without participating in task execution of the containing build. */ public interface StandAloneNestedBuild extends NestedBuildState, BuildActionTarget { + BuildState getOwner(); } diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildInclusionCoordinator.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildInclusionCoordinator.java index 3b6aa578ef79..bd6cb8a3754c 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildInclusionCoordinator.java +++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildInclusionCoordinator.java @@ -16,11 +16,15 @@ package org.gradle.internal.buildtree; -import org.gradle.internal.build.BuildStateRegistry; +import org.gradle.internal.build.BuildState; +import org.gradle.internal.build.CompositeBuildParticipantBuildState; import org.gradle.internal.build.IncludedBuildState; +import org.gradle.internal.build.RootBuildState; +import org.gradle.internal.composite.IncludedBuildInternal; import org.gradle.internal.service.scopes.Scopes; import org.gradle.internal.service.scopes.ServiceScope; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; @@ -33,15 +37,17 @@ public class BuildInclusionCoordinator { private final Set loadedBuilds = new CopyOnWriteArraySet<>(); private final List libraryBuilds = new CopyOnWriteArrayList<>(); - private final BuildStateRegistry buildStateRegistry; + private final GlobalDependencySubstitutionRegistry substitutionRegistry; + private boolean registerRootSubstitutions; + private final Set registering = new HashSet<>(); - public BuildInclusionCoordinator(BuildStateRegistry buildStateRegistry) { - this.buildStateRegistry = buildStateRegistry; + public BuildInclusionCoordinator(GlobalDependencySubstitutionRegistry substitutionRegistry) { + this.substitutionRegistry = substitutionRegistry; } public void prepareForInclusion(IncludedBuildState build, boolean asPlugin) { if (loadedBuilds.add(build)) { - // Load projects (eg by running the settings script, etc) only the first time the build is included by another build. + // Load projects (e.g. by running the settings script, etc.) only the first time the build is included by another build. // This is to deal with cycles and the build being included multiple times in the tree build.ensureProjectsLoaded(); } @@ -50,9 +56,70 @@ public void prepareForInclusion(IncludedBuildState build, boolean asPlugin) { } } - public void registerGlobalLibrarySubstitutions() { + public void prepareRootBuildForInclusion() { + registerRootSubstitutions = true; + } + + private void registerGlobalLibrarySubstitutions() { for (IncludedBuildState includedBuild : libraryBuilds) { - buildStateRegistry.registerSubstitutionsFor(includedBuild); + doRegisterSubstitutions(includedBuild); + } + } + + public void registerSubstitutionsAvailableFor(BuildState build) { + if (build instanceof RootBuildState) { + registerGlobalLibrarySubstitutions(); + } else { + makeSubstitutionsAvailableFor(build, new HashSet<>()); + } + } + + public void registerSubstitutionsProvidedBy(BuildState build) { + if (build instanceof RootBuildState && registerRootSubstitutions) { + // Make root build substitutions available + doRegisterSubstitutions((RootBuildState) build); + } + } + + public void prepareForPluginResolution(IncludedBuildState build) { + if (!registering.add(build)) { + return; + } + try { + build.ensureProjectsConfigured(); + makeSubstitutionsAvailableFor(build, new HashSet<>()); + } finally { + registering.remove(build); + } + } + + private void makeSubstitutionsAvailableFor(BuildState build, Set seen) { + // A build can see all the builds that it includes + if (!seen.add(build)) { + return; + } + boolean added = registering.add(build); + try { + for (IncludedBuildInternal reference : build.getMutableModel().includedBuilds()) { + BuildState target = reference.getTarget(); + if (!registering.contains(target) && target instanceof IncludedBuildState) { + doRegisterSubstitutions((IncludedBuildState) target); + makeSubstitutionsAvailableFor(target, seen); + } + } + } finally { + if (added) { + registering.remove(build); + } + } + } + + private void doRegisterSubstitutions(CompositeBuildParticipantBuildState build) { + registering.add(build); + try { + substitutionRegistry.registerSubstitutionsFor(build); + } finally { + registering.remove(build); } } } 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 f1a18744a316..13fd8df824fd 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 @@ -17,10 +17,13 @@ package org.gradle.internal.buildtree; import org.gradle.StartParameter; +import org.gradle.api.internal.cache.DefaultDecompressionCacheFactory; import org.gradle.api.internal.project.DefaultProjectStateRegistry; 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.BuildTreeScopedCache; import org.gradle.execution.DefaultTaskSelector; import org.gradle.execution.ProjectConfigurer; import org.gradle.execution.TaskNameResolver; @@ -95,4 +98,8 @@ protected ExceptionAnalyser createExceptionAnalyser(LoggingConfiguration logging } return exceptionAnalyser; } + + protected DecompressionCacheFactory createDecompressionCacheFactory(BuildTreeScopedCache cacheFactory) { + return new DefaultDecompressionCacheFactory(cacheFactory); + } } diff --git a/subprojects/core/src/main/java/org/gradle/initialization/ModelConfigurationListener.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/GlobalDependencySubstitutionRegistry.java similarity index 58% rename from subprojects/core/src/main/java/org/gradle/initialization/ModelConfigurationListener.java rename to subprojects/core/src/main/java/org/gradle/internal/buildtree/GlobalDependencySubstitutionRegistry.java index 9f4ca97674a1..4ab85a3dd1f0 100644 --- a/subprojects/core/src/main/java/org/gradle/initialization/ModelConfigurationListener.java +++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/GlobalDependencySubstitutionRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * 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. @@ -13,16 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.gradle.initialization; -import org.gradle.api.internal.GradleInternal; -import org.gradle.internal.service.scopes.EventScope; +package org.gradle.internal.buildtree; + +import org.gradle.internal.build.CompositeBuildParticipantBuildState; import org.gradle.internal.service.scopes.Scopes; +import org.gradle.internal.service.scopes.ServiceScope; -@EventScope(Scopes.Build.class) -public interface ModelConfigurationListener { - /** - * Invoked when the model has been configured successfully. This listener should not do any further configuration. - */ - void onConfigure(GradleInternal model); +@ServiceScope(Scopes.BuildTree.class) +public interface GlobalDependencySubstitutionRegistry { + void registerSubstitutionsFor(CompositeBuildParticipantBuildState build); } 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 0f804fcb7ddb..cb9285208be0 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 @@ -254,11 +254,20 @@ private void generateLambdaDeserializeMethod() { _F_SAME(); } next = new Label(); + Handle implHandle = (Handle) factory.bootstrapMethodArguments.get(1); + _ALOAD(0); _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getImplMethodName", RETURN_STRING); - _LDC(((Handle) factory.bootstrapMethodArguments.get(1)).getName()); + _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); @@ -524,7 +533,7 @@ private Optional getInstrumentedDescriptorForFileInputStreamConstructor( @Override public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { - if (isGradleLambdaDescriptor(descriptor) && bootstrapMethodHandle.getOwner().equals(LAMBDA_METAFACTORY_TYPE) && bootstrapMethodHandle.getName().equals("metafactory")) { + if (bootstrapMethodHandle.getOwner().equals(LAMBDA_METAFACTORY_TYPE) && bootstrapMethodHandle.getName().equals("metafactory")) { Handle altMethod = new Handle( H_INVOKESTATIC, LAMBDA_METAFACTORY_TYPE, diff --git a/subprojects/core/src/main/java/org/gradle/internal/composite/ChildBuildRegisteringSettingsLoader.java b/subprojects/core/src/main/java/org/gradle/internal/composite/ChildBuildRegisteringSettingsLoader.java index c7b5ae88afb1..a4c063c6d20d 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/composite/ChildBuildRegisteringSettingsLoader.java +++ b/subprojects/core/src/main/java/org/gradle/internal/composite/ChildBuildRegisteringSettingsLoader.java @@ -21,9 +21,7 @@ import org.gradle.initialization.SettingsLoader; import org.gradle.initialization.SettingsState; import org.gradle.internal.build.BuildIncluder; -import org.gradle.internal.build.BuildStateRegistry; -import org.gradle.internal.build.IncludedBuildState; -import org.gradle.internal.build.RootBuildState; +import org.gradle.internal.build.CompositeBuildParticipantBuildState; import java.util.Collections; import java.util.LinkedHashSet; @@ -33,13 +31,11 @@ public class ChildBuildRegisteringSettingsLoader implements SettingsLoader { private final SettingsLoader delegate; - private final BuildStateRegistry buildRegistry; private final BuildIncluder buildIncluder; - public ChildBuildRegisteringSettingsLoader(SettingsLoader delegate, BuildStateRegistry buildRegistry, BuildIncluder buildIncluder) { + public ChildBuildRegisteringSettingsLoader(SettingsLoader delegate, BuildIncluder buildIncluder) { this.delegate = delegate; - this.buildRegistry = buildRegistry; this.buildIncluder = buildIncluder; } @@ -51,15 +47,9 @@ public SettingsState findAndLoadSettings(GradleInternal gradle) { List includedBuilds = state.getSettings().getIncludedBuilds(); if (!includedBuilds.isEmpty()) { Set children = new LinkedHashSet<>(includedBuilds.size()); - RootBuildState rootBuild = buildRegistry.getRootBuild(); for (IncludedBuildSpec includedBuildSpec : includedBuilds) { - if (!includedBuildSpec.rootDir.equals(rootBuild.getBuildRootDir())) { - IncludedBuildState includedBuild = buildIncluder.includeBuild(includedBuildSpec); - children.add(includedBuild.getModel()); - } else { - buildRegistry.registerSubstitutionsForRootBuild(); - children.add(rootBuild.getModel()); - } + CompositeBuildParticipantBuildState includedBuild = buildIncluder.includeBuild(includedBuildSpec); + children.add(includedBuild.getModel()); } // Set the visible included builds diff --git a/subprojects/core/src/main/java/org/gradle/internal/composite/DefaultBuildIncluder.java b/subprojects/core/src/main/java/org/gradle/internal/composite/DefaultBuildIncluder.java index 71467db548e5..2f19653b6523 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/composite/DefaultBuildIncluder.java +++ b/subprojects/core/src/main/java/org/gradle/internal/composite/DefaultBuildIncluder.java @@ -20,9 +20,12 @@ import org.gradle.api.internal.GradleInternal; import org.gradle.initialization.IncludedBuildSpec; import org.gradle.internal.build.BuildIncluder; +import org.gradle.internal.build.BuildState; import org.gradle.internal.build.BuildStateRegistry; +import org.gradle.internal.build.CompositeBuildParticipantBuildState; import org.gradle.internal.build.IncludedBuildState; import org.gradle.internal.build.PublicBuildPath; +import org.gradle.internal.build.RootBuildState; import org.gradle.internal.buildtree.BuildInclusionCoordinator; import org.gradle.internal.reflect.Instantiator; @@ -49,11 +52,17 @@ public DefaultBuildIncluder(BuildStateRegistry buildRegistry, BuildInclusionCoor } @Override - public IncludedBuildState includeBuild(IncludedBuildSpec includedBuildSpec) { - BuildDefinition buildDefinition = toBuildDefinition(includedBuildSpec, gradle); - IncludedBuildState build = buildRegistry.addIncludedBuild(buildDefinition); - coordinator.prepareForInclusion(build, buildDefinition.isPluginBuild()); - return build; + public CompositeBuildParticipantBuildState includeBuild(IncludedBuildSpec includedBuildSpec) { + RootBuildState rootBuild = buildRegistry.getRootBuild(); + if (includedBuildSpec.rootDir.equals(rootBuild.getBuildRootDir())) { + coordinator.prepareRootBuildForInclusion(); + return rootBuild; + } else { + BuildDefinition buildDefinition = toBuildDefinition(includedBuildSpec, gradle); + IncludedBuildState build = buildRegistry.addIncludedBuild(buildDefinition); + coordinator.prepareForInclusion(build, buildDefinition.isPluginBuild()); + return build; + } } @Override @@ -62,7 +71,7 @@ public void registerPluginBuild(IncludedBuildSpec includedBuildSpec) { } @Override - public Collection includeRegisteredPluginBuilds() { + public Collection getRegisteredPluginBuilds() { return pluginBuildDefinitions.stream().map(buildDefinition -> { IncludedBuildState build = buildRegistry.addIncludedBuild(buildDefinition); coordinator.prepareForInclusion(build, true); @@ -70,6 +79,19 @@ public Collection includeRegisteredPluginBuilds() { }).collect(Collectors.toList()); } + @Override + public Collection getIncludedBuildsForPluginResolution() { + BuildState thisBuild = gradle.getOwner(); + return buildRegistry.getIncludedBuilds().stream().filter(build -> + build != thisBuild && !build.isImplicitBuild() && !build.isPluginBuild() + ).collect(Collectors.toList()); + } + + @Override + public void prepareForPluginResolution(IncludedBuildState build) { + coordinator.prepareForPluginResolution(build); + } + private BuildDefinition toBuildDefinition(IncludedBuildSpec includedBuildSpec, GradleInternal gradle) { gradle.getOwner().assertCanAdd(includedBuildSpec); return includedBuildSpec.toBuildDefinition(gradle.getStartParameter(), publicBuildPath, instantiator); 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 9fc718e60c15..25c770707985 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 @@ -27,7 +27,6 @@ import org.gradle.api.internal.ExternalProcessStartedListener; import org.gradle.api.internal.StartParameterInternal; import org.gradle.api.internal.artifacts.DefaultModule; -import org.gradle.api.internal.artifacts.DependencyManagementServices; import org.gradle.api.internal.artifacts.Module; import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider; import org.gradle.api.internal.classpath.ModuleRegistry; @@ -42,8 +41,6 @@ import org.gradle.api.internal.file.temp.TemporaryFileProvider; import org.gradle.api.internal.initialization.DefaultScriptClassPathResolver; import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory; -import org.gradle.api.internal.initialization.ScriptClassPathInitializer; -import org.gradle.api.internal.initialization.ScriptClassPathResolver; import org.gradle.api.internal.initialization.ScriptHandlerFactory; import org.gradle.api.internal.model.NamedObjectInstantiator; import org.gradle.api.internal.plugins.DefaultPluginRegistry; @@ -131,7 +128,6 @@ import org.gradle.initialization.IGradlePropertiesLoader; import org.gradle.initialization.InitScriptHandler; import org.gradle.initialization.InstantiatingBuildLoader; -import org.gradle.initialization.ModelConfigurationListener; import org.gradle.initialization.NotifyingBuildLoader; import org.gradle.initialization.ProjectPropertySettingBuildLoader; import org.gradle.initialization.RootBuildCacheControllerSettingsProcessor; @@ -230,6 +226,8 @@ public BuildScopeServices(ServiceRegistry parent, BuildModelControllerServices.S registration.add(TaskDependencyResolver.class); registration.add(DefaultBuildWorkGraphController.class); registration.add(DefaultBuildIncluder.class); + registration.add(DefaultScriptClassPathResolver.class); + registration.add(DefaultScriptHandlerFactory.class); supplier.applyServicesTo(registration, this); for (PluginServiceRegistry pluginServiceRegistry : parent.getAll(PluginServiceRegistry.class)) { pluginServiceRegistry.registerBuildServices(registration); @@ -463,16 +461,17 @@ private DefaultScriptPluginFactory defaultScriptPluginFactory() { get(CompileOperationFactory.class)); } - protected BuildSourceBuilder createBuildSourceBuilder(BuildState currentBuild, FileLockManager fileLockManager, BuildOperationExecutor buildOperationExecutor, CachedClasspathTransformer cachedClasspathTransformer, CachingServiceLocator cachingServiceLocator, BuildStateRegistry buildRegistry, PublicBuildPath publicBuildPath) { + protected BuildSourceBuilder createBuildSourceBuilder(BuildState currentBuild, FileLockManager fileLockManager, BuildOperationExecutor buildOperationExecutor, CachedClasspathTransformer cachedClasspathTransformer, CachingServiceLocator cachingServiceLocator, BuildStateRegistry buildRegistry, PublicBuildPath publicBuildPath, NamedObjectInstantiator instantiator) { return new BuildSourceBuilder( currentBuild, fileLockManager, buildOperationExecutor, - cachedClasspathTransformer, new BuildSrcBuildListenerFactory( PluginsProjectConfigureActions.of( BuildSrcProjectConfigurationAction.class, - cachingServiceLocator)), + cachingServiceLocator), + instantiator, + cachedClasspathTransformer), buildRegistry, publicBuildPath); } @@ -525,39 +524,20 @@ protected SettingsPreparer createSettingsPreparer(SettingsLoaderFactory settings buildDefinition.getFromBuild()); } - protected ScriptClassPathResolver createScriptClassPathResolver(List initializers) { - return new DefaultScriptClassPathResolver(initializers); - } - - protected ScriptHandlerFactory createScriptHandlerFactory(DependencyManagementServices dependencyManagementServices, FileResolver fileResolver, FileCollectionFactory fileCollectionFactory, DependencyMetaDataProvider dependencyMetaDataProvider, ScriptClassPathResolver classPathResolver, NamedObjectInstantiator instantiator) { - return new DefaultScriptHandlerFactory( - dependencyManagementServices, - fileResolver, - fileCollectionFactory, - dependencyMetaDataProvider, - classPathResolver, - instantiator); - } - protected ProjectsPreparer createBuildConfigurer( ProjectConfigurer projectConfigurer, BuildSourceBuilder buildSourceBuilder, - BuildStateRegistry buildStateRegistry, BuildInclusionCoordinator inclusionCoordinator, BuildLoader buildLoader, - ListenerManager listenerManager, BuildOperationExecutor buildOperationExecutor, BuildModelParameters buildModelParameters ) { - ModelConfigurationListener modelConfigurationListener = listenerManager.getBroadcaster(ModelConfigurationListener.class); return new BuildOperationFiringProjectsPreparer( new BuildTreePreparingProjectsPreparer( new DefaultProjectsPreparer( projectConfigurer, buildModelParameters, - modelConfigurationListener, - buildOperationExecutor, - buildStateRegistry), + buildOperationExecutor), buildLoader, inclusionCoordinator, buildSourceBuilder), 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 67f6d21b7c24..3883f77d7b34 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 @@ -34,9 +34,7 @@ import org.gradle.cache.CacheRepository; import org.gradle.cache.GlobalCache; import org.gradle.cache.GlobalCacheLocations; -import org.gradle.cache.internal.CacheFactory; import org.gradle.cache.internal.CrossBuildInMemoryCacheFactory; -import org.gradle.cache.internal.DefaultCacheRepository; import org.gradle.cache.internal.DefaultFileContentCacheFactory; import org.gradle.cache.internal.DefaultGeneratedGradleJarCache; import org.gradle.cache.internal.DefaultGlobalCacheLocations; @@ -44,7 +42,6 @@ import org.gradle.cache.internal.GradleUserHomeCacheCleanupActionDecorator; import org.gradle.cache.internal.GradleUserHomeCleanupServices; import org.gradle.cache.internal.InMemoryCacheDecoratorFactory; -import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultGlobalScopedCache; import org.gradle.cache.scopes.GlobalScopedCache; import org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache; @@ -103,7 +100,7 @@ public GradleUserHomeScopeServices(ServiceRegistry globalServices) { } public void configure(ServiceRegistration registration) { - registration.add(GlobalCacheDir.class); + super.configure(registration); registration.addProvider(new GradleUserHomeCleanupServices()); registration.add(ClasspathWalker.class); registration.add(ClasspathBuilder.class); @@ -120,10 +117,6 @@ GradleUserHomeCacheCleanupActionDecorator createCacheCleanupDecorator(GradleUser return new GradleUserHomeCacheCleanupActionDecorator(gradleUserHomeDirProvider); } - CacheRepository createCacheRepository(GlobalCacheDir globalCacheDir, CacheFactory cacheFactory) { - return new DefaultCacheRepository(new DefaultCacheScopeMapping(globalCacheDir.getDir(), GradleVersion.current()), cacheFactory); - } - DefaultGlobalScopedCache createGlobalScopedCache(GlobalCacheDir globalCacheDir, CacheRepository cacheRepository) { return new DefaultGlobalScopedCache(globalCacheDir.getDir(), cacheRepository); } 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 cc0da0ca94e7..ea9a8ddb9776 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 @@ -42,7 +42,6 @@ import org.gradle.api.internal.initialization.ScriptClassPathResolver; import org.gradle.api.internal.initialization.ScriptHandlerFactory; import org.gradle.api.internal.initialization.ScriptHandlerInternal; -import org.gradle.api.internal.model.NamedObjectInstantiator; import org.gradle.api.internal.plugins.DefaultPluginManager; import org.gradle.api.internal.plugins.PluginManagerInternal; import org.gradle.api.internal.plugins.PluginRegistry; @@ -222,14 +221,14 @@ protected ModelRegistry createModelRegistry(ModelRuleExtractor ruleExtractor) { return new DefaultModelRegistry(ruleExtractor, project.getPath(), run -> project.getOwner().applyToMutableState(p -> run.run())); } - protected ScriptHandlerInternal createScriptHandler(DependencyManagementServices dependencyManagementServices, FileResolver fileResolver, FileCollectionFactory fileCollectionFactory, DependencyMetaDataProvider dependencyMetaDataProvider, ScriptClassPathResolver scriptClassPathResolver, NamedObjectInstantiator instantiator) { + + protected ScriptHandlerInternal createScriptHandler(DependencyManagementServices dependencyManagementServices, FileResolver fileResolver, FileCollectionFactory fileCollectionFactory, DependencyMetaDataProvider dependencyMetaDataProvider, ScriptClassPathResolver scriptClassPathResolver) { ScriptHandlerFactory factory = new DefaultScriptHandlerFactory( dependencyManagementServices, fileResolver, fileCollectionFactory, dependencyMetaDataProvider, - scriptClassPathResolver, - instantiator); + scriptClassPathResolver); return factory.create(project.getBuildScriptSource(), project.getClassLoaderScope(), new ScriptScopedContext(project)); } diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedProjectScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedProjectScopeServices.java index df3f7b7bb1eb..9c2ff24cc96a 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedProjectScopeServices.java +++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedProjectScopeServices.java @@ -19,6 +19,7 @@ import org.gradle.api.file.ArchiveOperations; import org.gradle.api.file.FileSystemOperations; import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.cache.DefaultDecompressionCacheFactory; import org.gradle.api.internal.collections.DomainObjectCollectionFactory; import org.gradle.api.internal.file.DefaultArchiveOperations; import org.gradle.api.internal.file.DefaultFileCollectionFactory; @@ -44,10 +45,13 @@ import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.tasks.util.PatternSet; +import org.gradle.cache.CacheRepository; +import org.gradle.cache.internal.DecompressionCacheFactory; +import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; +import org.gradle.cache.scopes.ProjectScopedCache; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; -import org.gradle.internal.hash.StreamHasher; import org.gradle.internal.instantiation.InstantiatorFactory; import org.gradle.internal.nativeintegration.filesystem.FileSystem; import org.gradle.internal.reflect.Instantiator; @@ -81,10 +85,8 @@ protected FileResolver createFileResolver(FileLookup lookup) { protected DefaultFileOperations createFileOperations( FileResolver fileResolver, - TemporaryFileProvider temporaryFileProvider, Instantiator instantiator, DirectoryFileTreeFactory directoryFileTreeFactory, - StreamHasher streamHasher, FileHasher fileHasher, DefaultResourceHandler.Factory resourceHandlerFactory, FileCollectionFactory fileCollectionFactory, @@ -94,14 +96,13 @@ protected DefaultFileOperations createFileOperations( Deleter deleter, DocumentationRegistry documentationRegistry, ProviderFactory providers, - TaskDependencyFactory taskDependencyFactory + TaskDependencyFactory taskDependencyFactory, + DecompressionCacheFactory decompressionCache ) { return new DefaultFileOperations( fileResolver, - temporaryFileProvider, instantiator, directoryFileTreeFactory, - streamHasher, fileHasher, resourceHandlerFactory, fileCollectionFactory, @@ -110,7 +111,9 @@ protected DefaultFileOperations createFileOperations( patternSetFactory, deleter, documentationRegistry, - taskDependencyFactory, providers); + taskDependencyFactory, + providers, + decompressionCache); } protected FileSystemOperations createFileSystemOperations(Instantiator instantiator, FileOperations fileOperations) { @@ -144,4 +147,11 @@ DefaultProjectLayout createProjectLayout(FileResolver fileResolver, FileCollecti FilePropertyFactory filePropertyFactory, Factory patternSetFactory, PropertyHost propertyHost, FileFactory fileFactory) { return new DefaultProjectLayout(projectDir, fileResolver, taskDependencyFactory, patternSetFactory, propertyHost, fileCollectionFactory, filePropertyFactory, fileFactory); } + + protected ProjectScopedCache createProjectScopedCache(TemporaryFileProvider temporaryFileProvider, CacheRepository cacheRepository) { + return new DefaultProjectScopedCache(temporaryFileProvider.newTemporaryFile(".cache"), cacheRepository); + } + protected DecompressionCacheFactory createDecompressionCacheFactory(ProjectScopedCache cacheFactory) { + return new DefaultDecompressionCacheFactory(cacheFactory); + } } diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedUserHomeScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedUserHomeScopeServices.java index 18a46fb1d94e..61d1b9ffcefb 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedUserHomeScopeServices.java +++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/WorkerSharedUserHomeScopeServices.java @@ -16,12 +16,26 @@ package org.gradle.internal.service.scopes; +import org.gradle.cache.CacheRepository; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.cache.internal.DefaultCacheRepository; +import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; +import org.gradle.initialization.layout.GlobalCacheDir; import org.gradle.internal.hash.ClassLoaderHierarchyHasher; import org.gradle.internal.isolation.IsolatableFactory; +import org.gradle.internal.service.ServiceRegistration; import org.gradle.internal.snapshot.impl.DefaultIsolatableFactory; import org.gradle.internal.state.ManagedFactoryRegistry; +import org.gradle.util.GradleVersion; public class WorkerSharedUserHomeScopeServices { + public void configure(ServiceRegistration registration) { + registration.add(GlobalCacheDir.class); + } + + CacheRepository createCacheRepository(GlobalCacheDir globalCacheDir, CacheFactory cacheFactory) { + return new DefaultCacheRepository(new DefaultCacheScopeMapping(globalCacheDir.getDir(), GradleVersion.current()), cacheFactory); + } IsolatableFactory createIsolatableFactory( ClassLoaderHierarchyHasher classLoaderHierarchyHasher, diff --git a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestInMemoryCacheFactory.java b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestInMemoryCacheFactory.java index 50b397398600..ba7f624e20c0 100644 --- a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestInMemoryCacheFactory.java +++ b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestInMemoryCacheFactory.java @@ -162,7 +162,10 @@ public T useCache(Factory action) { @Override public void useCache(Runnable action) { assertNotClosed(); - action.run(); + // The contract of useCache() means we have to provide some basic synchronization. + synchronized (this) { + action.run(); + } } @Override diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/cache/DefaultCacheConfigurationsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/cache/DefaultCacheConfigurationsTest.groovy index 9cf51f7a5ca5..e6d3b6684aef 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/cache/DefaultCacheConfigurationsTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/cache/DefaultCacheConfigurationsTest.groovy @@ -25,49 +25,101 @@ import static org.gradle.internal.time.TimestampSuppliers.daysAgo class DefaultCacheConfigurationsTest extends Specification { def cacheConfigurations = TestUtil.objectFactory().newInstance(DefaultCacheConfigurations.class) - def "cannot modify cache configurations once finalized"() { + def "cannot modify cache configurations via convenience method unless mutable"() { when: - cacheConfigurations.createdResources.setRemoveUnusedEntriesAfterDays(2) - cacheConfigurations.downloadedResources.setRemoveUnusedEntriesAfterDays(2) - cacheConfigurations.releasedWrappers.setRemoveUnusedEntriesAfterDays(2) - cacheConfigurations.snapshotWrappers.setRemoveUnusedEntriesAfterDays(2) - cacheConfigurations.cleanup.set(Cleanup.DISABLED) + cacheConfigurations.withMutableValues { + cacheConfigurations.createdResources.setRemoveUnusedEntriesAfterDays(2) + cacheConfigurations.downloadedResources.setRemoveUnusedEntriesAfterDays(2) + cacheConfigurations.releasedWrappers.setRemoveUnusedEntriesAfterDays(2) + cacheConfigurations.snapshotWrappers.setRemoveUnusedEntriesAfterDays(2) + cacheConfigurations.cleanup.set(Cleanup.DISABLED) + } then: noExceptionThrown() when: - cacheConfigurations.finalizeConfigurations() - - and: cacheConfigurations.createdResources.setRemoveUnusedEntriesAfterDays(1) then: - thrown(IllegalStateException) + def e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") when: cacheConfigurations.downloadedResources.setRemoveUnusedEntriesAfterDays(1) then: - thrown(IllegalStateException) + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") when: cacheConfigurations.releasedWrappers.setRemoveUnusedEntriesAfterDays(1) then: - thrown(IllegalStateException) + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") when: cacheConfigurations.snapshotWrappers.setRemoveUnusedEntriesAfterDays(1) then: - thrown(IllegalStateException) + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") + } + + def "cannot modify cache configurations via property unless mutable (method: #method)"() { + long firstValue = 2 + long secondValue = 1 + + when: + cacheConfigurations.withMutableValues { + cacheConfigurations.createdResources.removeUnusedEntriesOlderThan."${method}"(firstValue) + cacheConfigurations.downloadedResources.removeUnusedEntriesOlderThan."${method}"(firstValue) + cacheConfigurations.releasedWrappers.removeUnusedEntriesOlderThan."${method}"(firstValue) + cacheConfigurations.snapshotWrappers.removeUnusedEntriesOlderThan."${method}"(firstValue) + cacheConfigurations.cleanup."${method}"(Cleanup.DISABLED) + } + + then: + noExceptionThrown() + + when: + cacheConfigurations.createdResources.removeUnusedEntriesOlderThan."${method}"(secondValue) + + then: + def e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") + + when: + cacheConfigurations.downloadedResources.removeUnusedEntriesOlderThan."${method}"(secondValue) + + then: + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") + + when: + cacheConfigurations.releasedWrappers.removeUnusedEntriesOlderThan."${method}"(secondValue) + + then: + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") when: - cacheConfigurations.cleanup.set(Cleanup.DEFAULT) + cacheConfigurations.snapshotWrappers.removeUnusedEntriesOlderThan."${method}"(secondValue) then: - thrown(IllegalStateException) + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "removeUnusedEntriesOlderThan") + + when: + cacheConfigurations.cleanup."${method}"(Cleanup.DEFAULT) + + then: + e = thrown(IllegalStateException) + assertCannotConfigureErrorIsThrown(e, "cleanup") + + where: + method << ["set", "value", "convention"] } def "suppliers reflect changes in property values"() { @@ -79,10 +131,12 @@ class DefaultCacheConfigurationsTest extends Specification { and: def twoDaysAgo = daysAgo(2).get() - cacheConfigurations.createdResources.removeUnusedEntriesOlderThan.set(twoDaysAgo) - cacheConfigurations.downloadedResources.removeUnusedEntriesOlderThan.set(twoDaysAgo) - cacheConfigurations.releasedWrappers.removeUnusedEntriesOlderThan.set(twoDaysAgo) - cacheConfigurations.snapshotWrappers.removeUnusedEntriesOlderThan.set(twoDaysAgo) + cacheConfigurations.withMutableValues { + cacheConfigurations.createdResources.removeUnusedEntriesOlderThan.set(twoDaysAgo) + cacheConfigurations.downloadedResources.removeUnusedEntriesOlderThan.set(twoDaysAgo) + cacheConfigurations.releasedWrappers.removeUnusedEntriesOlderThan.set(twoDaysAgo) + cacheConfigurations.snapshotWrappers.removeUnusedEntriesOlderThan.set(twoDaysAgo) + } then: createdResources.get() == twoDaysAgo @@ -93,27 +147,39 @@ class DefaultCacheConfigurationsTest extends Specification { def "cannot set values in days to less than one"() { when: - cacheConfigurations.createdResources.setRemoveUnusedEntriesAfterDays(0) + cacheConfigurations.withMutableValues { + cacheConfigurations.createdResources.setRemoveUnusedEntriesAfterDays(0) + } then: thrown(IllegalArgumentException) when: - cacheConfigurations.downloadedResources.setRemoveUnusedEntriesAfterDays(0) + cacheConfigurations.withMutableValues { + cacheConfigurations.downloadedResources.setRemoveUnusedEntriesAfterDays(0) + } then: thrown(IllegalArgumentException) when: - cacheConfigurations.releasedWrappers.setRemoveUnusedEntriesAfterDays(0) + cacheConfigurations.withMutableValues { + cacheConfigurations.releasedWrappers.setRemoveUnusedEntriesAfterDays(0) + } then: thrown(IllegalArgumentException) when: - cacheConfigurations.snapshotWrappers.setRemoveUnusedEntriesAfterDays(0) + cacheConfigurations.withMutableValues { + cacheConfigurations.snapshotWrappers.setRemoveUnusedEntriesAfterDays(0) + } then: thrown(IllegalArgumentException) } + + void assertCannotConfigureErrorIsThrown(Exception e, String name) { + assert e.message.contains(String.format(DefaultCacheConfigurations.ILLEGAL_MODIFICATION_ERROR, name)) + } } diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy index 68a6a7136389..25db5aed1459 100755 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/DefaultFileOperationsTest.groovy @@ -24,10 +24,11 @@ import org.gradle.api.internal.file.archive.ZipFileTree import org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory import org.gradle.api.internal.file.collections.FileTreeAdapter import org.gradle.api.internal.file.copy.DefaultCopySpec +import org.gradle.api.internal.file.temp.DefaultTemporaryFileProvider import org.gradle.api.internal.file.temp.TemporaryFileProvider import org.gradle.api.internal.resources.DefaultResourceHandler +import org.gradle.cache.internal.TestCaches import org.gradle.internal.hash.FileHasher -import org.gradle.internal.hash.StreamHasher import org.gradle.internal.reflect.Instantiator import org.gradle.test.fixtures.file.TestFile import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider @@ -35,31 +36,33 @@ import org.gradle.util.TestUtil import org.gradle.util.UsesNativeServices import org.junit.Rule import spock.lang.Specification +import spock.lang.TempDir @UsesNativeServices class DefaultFileOperationsTest extends Specification { + @Rule + public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()) + + @TempDir + File cacheDir + private final FileResolver resolver = Mock() { getPatternSetFactory() >> TestFiles.getPatternSetFactory() } - private final TemporaryFileProvider temporaryFileProvider = Mock() + private final TemporaryFileProvider temporaryFileProvider = new DefaultTemporaryFileProvider(() -> cacheDir); private final Instantiator instantiator = TestUtil.instantiatorFactory().decorateLenient() private final DefaultDirectoryFileTreeFactory directoryFileTreeFactory = Mock() - private final StreamHasher streamHasher = Mock() private final FileHasher fileHasher = Mock() private final DefaultResourceHandler.Factory resourceHandlerFactory = Mock() private final FileCollectionFactory fileCollectionFactory = Mock() private DefaultFileOperations fileOperations = instance() - @Rule - public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()) private DefaultFileOperations instance(FileResolver resolver = resolver) { instantiator.newInstance( DefaultFileOperations, resolver, - temporaryFileProvider, instantiator, directoryFileTreeFactory, - streamHasher, fileHasher, resourceHandlerFactory, fileCollectionFactory, @@ -69,7 +72,8 @@ class DefaultFileOperationsTest extends Specification { TestFiles.deleter(), TestFiles.documentationRegistry(), TestFiles.taskDependencyFactory(), - TestUtil.providerFactory() + TestUtil.providerFactory(), + TestCaches.decompressionCacheFactory(temporaryFileProvider.newTemporaryDirectory("cache")) ) } @@ -249,4 +253,3 @@ class DefaultFileOperationsTest extends Specification { return TestFiles.resolver(tmpDir.testDirectory) } } - diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy similarity index 79% rename from subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.groovy rename to subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy index 806b61b19b81..a1c9018d3bb0 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy @@ -21,12 +21,17 @@ import org.gradle.api.internal.file.FileTreeInternal import org.gradle.api.internal.file.collections.DirectoryFileTree import org.gradle.api.internal.file.collections.MinimalFileTree import org.gradle.api.provider.Provider +import org.gradle.cache.internal.DecompressionCache +import org.gradle.cache.internal.TestCaches import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider import org.gradle.util.TestUtil import org.junit.Rule import spock.lang.Specification -class AbstractArchiveFileTreeTest extends Specification { +/** + * Tests core functionality in {@link AbstractArchiveFileTree} using a minimal test implementation. + */ +class AbstractArchiveFileTreeSpec extends Specification { @Rule TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()) @@ -35,7 +40,7 @@ class AbstractArchiveFileTreeTest extends Specification { def visitor = Mock(MinimalFileTree.MinimalFileTreeStructureVisitor) def backingFile = tmpDir.createFile("thing.bin") - def fileTree = new TestArchiveFileTree(backingFile: backingFile) + def fileTree = new TestArchiveFileTree(TestCaches.decompressionCache(tmpDir.createDir("cache-dir")), backingFile) when: fileTree.visitStructure(visitor, owner) @@ -49,6 +54,11 @@ class AbstractArchiveFileTreeTest extends Specification { File backingFile final String displayName = "" + TestArchiveFileTree(DecompressionCache decompressionCache, File backingFile) { + super(decompressionCache) + this.backingFile = backingFile + } + @Override DirectoryFileTree getMirror() { throw new UnsupportedOperationException() diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.java new file mode 100644 index 000000000000..d117dad3b191 --- /dev/null +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.java @@ -0,0 +1,163 @@ +/* + * 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.file.archive; + +import org.apache.commons.io.FileUtils; +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.file.FileVisitor; +import org.gradle.test.fixtures.file.TestFile; +import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider; +import org.gradle.util.internal.Resources; +import org.junit.Rule; +import org.junit.Test; +import spock.lang.Issue; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +import static org.gradle.api.file.FileVisitorUtil.assertCanStopVisiting; +import static org.gradle.api.file.FileVisitorUtil.assertVisits; +import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes; +import static org.gradle.util.internal.WrapUtil.toList; +import static org.junit.Assert.assertEquals; + +/** + * Abstract base class for all tests of {@link AbstractArchiveFileTree} implementations which tests common + * functionality. + */ +public abstract class AbstractArchiveFileTreeTest { + @Rule public final TestNameTestDirectoryProvider tempDirProvider = new TestNameTestDirectoryProvider(getClass()); + @Rule public final Resources resources = new Resources(); + protected final TestFile tmpDir = tempDirProvider.getTestDirectory().file("tmp"); + protected final TestFile rootDir = tempDirProvider.getTestDirectory().file("root"); + + protected abstract TestFile getArchiveFile(); + protected abstract AbstractArchiveFileTree getTree(); + protected abstract void archiveFileToRoot(TestFile file); + + @Issue("https://github.com/gradle/gradle/issues/22685") + @Test + public void testConcurrentArchiveVisiting() throws InterruptedException { + Long numLines = 1000000L; + int numFiles = 2; + int numThreads = 3; + + // This visitor counts the lines in each file it visits, and makes available a list of all the line counts + class CountingVisitor implements FileVisitor { + private final List actualCounts = new ArrayList<>(); + + public List getActualCounts() { + return actualCounts; + } + + @Override + public void visitDir(FileVisitDetails dirDetails) { } + + @Override + public void visitFile(FileVisitDetails fileDetails) { + try { + List lines = FileUtils.readLines(fileDetails.getFile(), Charset.defaultCharset()); + actualCounts.add((long) lines.size()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + // Generate a sufficiently large zip file containing some large files with random numbers, one per line + Random random = new Random(); + for (int i = 0; i < numFiles; i++) { + StringBuilder sb = new StringBuilder(); + for (long j = 0; j < numLines; j++) { + sb.append(random.nextInt()).append('\n'); + } + rootDir.file("file" + i + ".txt").write(sb.toString()); + } + archiveFileToRoot(getArchiveFile()); + + // Create some visitors that will count the number of lines in each file they encounter + List visitors = new ArrayList<>(numThreads); + for (int i = 0; i < numThreads; i++) { + visitors.add(new CountingVisitor()); + } + + // Create callables that will send the visitors to visit the archive + List>> callables = visitors.stream().map(v -> { + return new Callable>() { + @Override + public List call() { + getTree().visit(v); + return v.getActualCounts(); + } + }; + }).collect(Collectors.toList()); + + // Concurrently visit the archive + ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + List>> results = executorService.invokeAll(callables); + + // And check that each visitor counted the complete number of lines in each file in the archive + // (i.e. that the archive was not visited by the second visitor before the first visitor had finished fully decompressing it) + results.forEach(f -> { + try { + f.get().forEach(result -> assertEquals("Files should only be read after full expansion when all lines are present", numLines, result)); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @Test + public void doesNotOverwriteFilesOnSecondVisit() { + TestFile contentFile = rootDir.file("file1.txt"); + contentFile.write("content"); + archiveFileToRoot(getArchiveFile()); + + assertVisits(getTree(), toList("file1.txt"), new ArrayList<>()); + contentFile.makeOlder(); + TestFile.Snapshot snapshot = contentFile.snapshot(); + assertVisits(getTree(), toList("file1.txt"), new ArrayList<>()); + contentFile.assertHasNotChangedSince(snapshot); + } + + @Test + public void visitsContentsOfArchiveFile() { + rootDir.file("subdir/file1.txt").write("content"); + rootDir.file("subdir2/file2.txt").write("content"); + archiveFileToRoot(getArchiveFile()); + + assertVisits(getTree(), toList("subdir/file1.txt", "subdir2/file2.txt"), toList("subdir", "subdir2")); + assertSetContainsForAllTypes(getTree(), toList("subdir/file1.txt", "subdir2/file2.txt")); + } + + @Test + public void canStopVisitingFiles() { + rootDir.file("subdir/file1.txt").write("content"); + rootDir.file("subdir/other/file2.txt").write("content"); + archiveFileToRoot(getArchiveFile()); + + assertCanStopVisiting(getTree()); + } +} diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java index bf3e15cce3ad..6d6e5a993a07 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/TarFileTreeTest.java @@ -17,23 +17,20 @@ import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; +import org.gradle.api.file.EmptyFileVisitor; import org.gradle.api.internal.file.MaybeCompressedFileResource; import org.gradle.api.internal.file.TestFiles; import org.gradle.api.provider.Provider; import org.gradle.api.resources.MissingResourceException; import org.gradle.api.resources.internal.LocalResourceAdapter; +import org.gradle.cache.internal.TestCaches; import org.gradle.test.fixtures.file.TestFile; -import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider; import org.gradle.util.TestUtil; -import org.gradle.util.internal.Resources; -import org.junit.Rule; import org.junit.Test; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import static org.gradle.api.file.FileVisitorUtil.assertCanStopVisiting; import static org.gradle.api.file.FileVisitorUtil.assertVisits; import static org.gradle.api.file.FileVisitorUtil.assertVisitsPermissions; import static org.gradle.api.internal.file.TestFiles.directoryFileTreeFactory; @@ -41,49 +38,56 @@ import static org.gradle.api.internal.file.TestFiles.fileSystem; import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes; import static org.gradle.util.internal.WrapUtil.toList; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.fail; -public class TarFileTreeTest { - @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()); - @Rule public final Resources resources = new Resources(); - private final TestFile tarFile = tmpDir.getTestDirectory().file("test.tar"); - private final TestFile rootDir = tmpDir.getTestDirectory().file("root"); - private final TestFile expandDir = tmpDir.getTestDirectory().file("tmp"); - private final TarFileTree tree = new TarFileTree(asProvider(tarFile), asProvider(new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tarFile)))), expandDir, fileSystem(), directoryFileTreeFactory(), fileHasher()); +public class TarFileTreeTest extends AbstractArchiveFileTreeTest { + private final TestFile archiveFile = tempDirProvider.getTestDirectory().file("test.tar"); + private final TarFileTree tree = new TarFileTree( + asProvider(archiveFile), + asProvider(new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(archiveFile)))), + fileSystem(), + directoryFileTreeFactory(), + fileHasher(), + TestCaches.decompressionCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); private static Provider asProvider(T object) { return TestUtil.providerFactory().provider(() -> object); } - @Test - public void displayName() { - assertThat(tree.getDisplayName(), equalTo("TAR '" + tarFile + "'")); + @Override + protected void archiveFileToRoot(TestFile file) { + rootDir.tarTo(file); } - @Test - public void visitsContentsOfTarFile() { - rootDir.file("subdir/file1.txt").write("content"); - rootDir.file("subdir2/file2.txt").write("content"); - rootDir.tarTo(tarFile); + @Override + protected TestFile getArchiveFile() { + return archiveFile; + } - assertVisits(tree, toList("subdir/file1.txt", "subdir2/file2.txt"), toList("subdir", "subdir2")); - assertSetContainsForAllTypes(tree, toList("subdir/file1.txt", "subdir2/file2.txt")); + @Override + protected TarFileTree getTree() { + return tree; + } + + @Test + public void displayName() { + assertThat(tree.getDisplayName(), equalTo("TAR '" + archiveFile + "'")); } @Test public void readsGzippedTarFile() { - TestFile tgz = tmpDir.getTestDirectory().file("test.tgz"); + TestFile tgz = tempDirProvider.getTestDirectory().file("test.tgz"); rootDir.file("subdir/file1.txt").write("content"); rootDir.file("subdir2/file2.txt").write("content"); rootDir.tgzTo(tgz); MaybeCompressedFileResource resource = new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tgz))); - TarFileTree tree = new TarFileTree(asProvider(tgz), asProvider(resource), expandDir, fileSystem(), directoryFileTreeFactory(), fileHasher()); + TarFileTree tree = new TarFileTree(asProvider(tgz), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), TestCaches.decompressionCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); assertVisits(tree, toList("subdir/file1.txt", "subdir2/file2.txt"), toList("subdir", "subdir2")); assertSetContainsForAllTypes(tree, toList("subdir/file1.txt", "subdir2/file2.txt")); @@ -91,69 +95,61 @@ public void readsGzippedTarFile() { @Test public void readsBzippedTarFile() { - TestFile tbz2 = tmpDir.getTestDirectory().file("test.tbz2"); + TestFile tbz2 = tempDirProvider.getTestDirectory().file("test.tbz2"); rootDir.file("subdir/file1.txt").write("content"); rootDir.file("subdir2/file2.txt").write("content"); rootDir.tbzTo(tbz2); MaybeCompressedFileResource resource = new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tbz2))); - TarFileTree tree = new TarFileTree(asProvider(tbz2), asProvider(resource), expandDir, fileSystem(), directoryFileTreeFactory(), fileHasher()); + TarFileTree tree = new TarFileTree(asProvider(tbz2), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), TestCaches.decompressionCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); assertVisits(tree, toList("subdir/file1.txt", "subdir2/file2.txt"), toList("subdir", "subdir2")); assertSetContainsForAllTypes(tree, toList("subdir/file1.txt", "subdir2/file2.txt")); } @Test - public void canStopVisitingFiles() { - rootDir.file("subdir/file1.txt").write("content"); - rootDir.file("subdir/other/file2.txt").write("content"); - rootDir.tarTo(tarFile); - - assertCanStopVisiting(tree); - } - - @Test - public void failsWhenTarFileDoesNotExist() { + public void failsWhenArchiveFileDoesNotExist() { try { - tree.visit(null); + tree.visit(new EmptyFileVisitor()); fail(); } catch (InvalidUserDataException e) { - assertThat(e.getMessage(), containsString("Cannot expand TAR '" + tarFile + "'.")); + assertThat(e.getMessage(), containsString("Cannot expand TAR '" + archiveFile + "'.")); assertThat(e.getCause(), instanceOf(MissingResourceException.class)); } } @Test - public void failsWhenTarFileIsADirectory() { - tarFile.createDir(); + public void failsWhenArchiveFileIsADirectory() { + archiveFile.createDir(); try { - tree.visit(null); + tree.visit(new EmptyFileVisitor()); fail(); } catch (InvalidUserDataException e) { - assertThat(e.getMessage(), containsString("Cannot expand TAR '" + tarFile + "'")); + assertThat(e.getMessage(), containsString("Cannot expand TAR '" + archiveFile + "'")); } } @Test - public void wrapsFailureToUntarFile() { - expandDir.write("not a directory"); - tarFile.write("not a tar file"); + public void wrapsFailureToUnarchiveFile() { + tmpDir.write("not a directory"); + archiveFile.write("not a tar file"); try { - tree.visit(null); + tree.visit(new EmptyFileVisitor()); fail(); } catch (GradleException e) { - assertThat(e.getMessage(), containsString("Unable to expand TAR '" + tarFile + "'")); + assertThat(e.getMessage(), containsString("Unable to expand TAR '" + archiveFile + "'")); } } + @Test public void expectedFilePermissionsAreFound() { - resources.findResource("permissions.tar").copyTo(tarFile); + resources.findResource("permissions.tar").copyTo(archiveFile); - final Map expected = new HashMap(); + final Map expected = new HashMap<>(); expected.put("file", 0644); expected.put("folder", 0755); @@ -162,24 +158,11 @@ public void expectedFilePermissionsAreFound() { @Test public void readsTarFileWithNullPermissions() { - resources.findResource("nullpermissions.tar").copyTo(tarFile); + resources.findResource("nullpermissions.tar").copyTo(archiveFile); final Map expected = new HashMap(); expected.put("bin", 0755); assertVisitsPermissions(tree, expected); } - - @Test - public void doesNotOverwriteFilesOnSecondVisit() throws InterruptedException { - rootDir.file("file1.txt").write("content"); - rootDir.tarTo(tarFile); - - assertVisits(tree, toList("file1.txt"), new ArrayList()); - TestFile content = expandDir.listFiles()[0].listFiles()[0]; - content.makeOlder(); - TestFile.Snapshot snapshot = content.snapshot(); - assertVisits(tree, toList("file1.txt"), new ArrayList()); - content.assertHasNotChangedSince(snapshot); - } } diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java index bb02f353a8f9..402494ee737d 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/ZipFileTreeTest.java @@ -18,94 +18,89 @@ import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; import org.gradle.api.file.EmptyFileVisitor; +import org.gradle.cache.internal.TestCaches; import org.gradle.test.fixtures.file.TestFile; -import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider; import org.gradle.util.TestUtil; -import org.gradle.util.internal.Resources; -import org.junit.Rule; import org.junit.Test; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import static org.gradle.api.file.FileVisitorUtil.*; -import static org.gradle.api.internal.file.TestFiles.*; -import static org.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes; -import static org.gradle.util.internal.WrapUtil.toList; +import static org.gradle.api.file.FileVisitorUtil.assertVisitsPermissions; +import static org.gradle.api.internal.file.TestFiles.directoryFileTreeFactory; +import static org.gradle.api.internal.file.TestFiles.fileHasher; +import static org.gradle.api.internal.file.TestFiles.fileSystem; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; -public class ZipFileTreeTest { - @Rule public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()); - @Rule public final Resources resources = new Resources(); - private final TestFile zipFile = tmpDir.getTestDirectory().file("test.zip"); - private final TestFile rootDir = tmpDir.getTestDirectory().file("root"); - private final TestFile expandDir = tmpDir.getTestDirectory().file("tmp"); - private final ZipFileTree tree = new ZipFileTree(TestUtil.providerFactory().provider(()->zipFile), expandDir, fileSystem(), directoryFileTreeFactory(), fileHasher()); - - @Test - public void displayName() { - assertThat(tree.getDisplayName(), equalTo("ZIP '" + zipFile + "'")); +public class ZipFileTreeTest extends AbstractArchiveFileTreeTest { + private final TestFile archiveFile = tempDirProvider.getTestDirectory().file("test.zip"); + private final ZipFileTree tree = new ZipFileTree( + TestUtil.providerFactory().provider(() -> archiveFile), + fileSystem(), + directoryFileTreeFactory(), + fileHasher(), + TestCaches.decompressionCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); + + @Override + protected void archiveFileToRoot(TestFile file) { + rootDir.zipTo(file); } - @Test - public void visitsContentsOfZipFile() { - rootDir.file("subdir/file1.txt").write("content"); - rootDir.file("subdir2/file2.txt").write("content"); - rootDir.zipTo(zipFile); + @Override + protected TestFile getArchiveFile() { + return archiveFile; + } - assertVisits(tree, toList("subdir/file1.txt", "subdir2/file2.txt"), toList("subdir", "subdir2")); - assertSetContainsForAllTypes(tree, toList("subdir/file1.txt", "subdir2/file2.txt")); + @Override + protected ZipFileTree getTree() { + return tree; } @Test - public void canStopVisitingFiles() { - rootDir.file("subdir/file1.txt").write("content"); - rootDir.file("subdir/other/file2.txt").write("content"); - rootDir.zipTo(zipFile); - - assertCanStopVisiting(tree); + public void displayName() { + assertThat(tree.getDisplayName(), equalTo("ZIP '" + archiveFile + "'")); } @Test - public void failsWhenZipFileDoesNotExist() { + public void failsWhenArchiveFileDoesNotExist() { try { - tree.visit(new EmptyFileVisitor()); + getTree().visit(new EmptyFileVisitor()); fail(); } catch (InvalidUserDataException e) { - assertThat(e.getMessage(), equalTo("Cannot expand ZIP '" + zipFile + "' as it does not exist.")); + assertThat(e.getMessage(), equalTo("Cannot expand " + getTree().getDisplayName() + " as it does not exist.")); } } @Test - public void failsWhenZipFileIsADirectory() { - zipFile.createDir(); + public void failsWhenArchiveFileIsADirectory() { + getArchiveFile().createDir(); try { - tree.visit(new EmptyFileVisitor()); + getTree().visit(new EmptyFileVisitor()); fail(); } catch (InvalidUserDataException e) { - assertThat(e.getMessage(), equalTo("Cannot expand ZIP '" + zipFile + "' as it is not a file.")); + assertThat(e.getMessage(), equalTo("Cannot expand " + getTree().getDisplayName() + " as it is not a file.")); } } @Test - public void wrapsFailureToUnzipFile() { - zipFile.write("not a zip file"); + public void wrapsFailureToUnarchiveFile() { + tmpDir.write("not a directory"); + getArchiveFile().write("not an archive file"); try { - tree.visit(new EmptyFileVisitor()); + getTree().visit(new EmptyFileVisitor()); fail(); } catch (GradleException e) { - assertThat(e.getMessage(), equalTo("Cannot expand ZIP '" + zipFile + "'.")); + assertThat(e.getMessage(), equalTo("Cannot expand " + getTree().getDisplayName() + ".")); } } @Test public void expectedFilePermissionsAreFound() { - resources.findResource("permissions.zip").copyTo(zipFile); + resources.findResource("permissions.zip").copyTo(archiveFile); final Map expected = new HashMap<>(); expected.put("file", 0644); @@ -116,7 +111,7 @@ public void expectedFilePermissionsAreFound() { @Test public void expectedDefaultForNoModeZips() { - resources.findResource("nomodeinfos.zip").copyTo(zipFile); + resources.findResource("nomodeinfos.zip").copyTo(archiveFile); final Map expected = new HashMap<>(); expected.put("file.txt", 0644); @@ -124,17 +119,4 @@ public void expectedDefaultForNoModeZips() { assertVisitsPermissions(tree, expected); } - - @Test - public void doesNotOverwriteFilesOnSecondVisit() { - rootDir.file("file1.txt").write("content"); - rootDir.zipTo(zipFile); - - assertVisits(tree, toList("file1.txt"), new ArrayList<>()); - TestFile content = expandDir.listFiles()[0].listFiles()[0]; - content.makeOlder(); - TestFile.Snapshot snapshot = content.snapshot(); - assertVisits(tree, toList("file1.txt"), new ArrayList<>()); - content.assertHasNotChangedSince(snapshot); - } } 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 1df5d8a47bb1..d31debeb6424 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 @@ -21,9 +21,6 @@ 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.attributes.Bundling -import org.gradle.api.attributes.Usage -import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.internal.artifacts.DependencyResolutionServices import org.gradle.api.internal.attributes.AttributeContainerInternal import org.gradle.api.internal.attributes.AttributesSchemaInternal @@ -31,7 +28,6 @@ import org.gradle.groovy.scripts.ScriptSource import org.gradle.internal.classloader.ClasspathUtil import org.gradle.internal.classpath.ClassPath import org.gradle.util.internal.ConfigureUtil -import org.gradle.util.TestUtil import spock.lang.Specification class DefaultScriptHandlerTest extends Specification { @@ -50,8 +46,7 @@ class DefaultScriptHandlerTest extends Specification { getLocalClassLoader() >> baseClassLoader } def classpathResolver = Mock(ScriptClassPathResolver) - def instantiator = TestUtil.objectInstantiator() - def handler = new DefaultScriptHandler(scriptSource, depMgmtServices, classLoaderScope, classpathResolver, instantiator) + def handler = new DefaultScriptHandler(scriptSource, depMgmtServices, classLoaderScope, classpathResolver) def attributes = Mock(AttributeContainerInternal) def "adds classpath configuration when configuration container is queried"() { @@ -63,14 +58,7 @@ class DefaultScriptHandlerTest extends Specification { 1 * depMgmtServices.configurationContainer >> configurationContainer 1 * depMgmtServices.dependencyHandler >> dependencyHandler 1 * configurationContainer.create('classpath') >> configuration - 1 * configuration.attributes >> attributes - 1 * attributes.attribute(Usage.USAGE_ATTRIBUTE, _ as Usage) - 1 * attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, _ as Bundling) - 1 * attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, _) - 1 * configuration.getDependencyConstraints() >> dependencyConstraintSet - 1 * dependencyConstraintSet.add(_) - 1 * dependencyHandler.getConstraints() >> dependencyConstraintHandler - 1 * dependencyConstraintHandler.create(_, _) + 1 * classpathResolver.prepareClassPath(configuration, dependencyHandler) 0 * configurationContainer._ 0 * depMgmtServices._ } @@ -84,21 +72,14 @@ class DefaultScriptHandlerTest extends Specification { 1 * depMgmtServices.configurationContainer >> configurationContainer 1 * depMgmtServices.dependencyHandler >> dependencyHandler 1 * configurationContainer.create('classpath') >> configuration - 1 * configuration.attributes >> attributes - 1 * attributes.attribute(Usage.USAGE_ATTRIBUTE, _ as Usage) - 1 * attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, _ as Bundling) - 1 * attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, _) - 1 * configuration.getDependencyConstraints() >> dependencyConstraintSet - 1 * dependencyConstraintSet.add(_) - 1 * dependencyHandler.getConstraints() >> dependencyConstraintHandler - 1 * dependencyConstraintHandler.create(_, _) + 1 * classpathResolver.prepareClassPath(configuration, dependencyHandler) 0 * configurationContainer._ 0 * depMgmtServices._ } def "does not resolve classpath configuration when configuration container has not been queried"() { when: - def classpath = handler.nonInstrumentedScriptClassPath + def classpath = handler.instrumentedScriptClassPath then: 0 * configuration._ @@ -113,7 +94,7 @@ class DefaultScriptHandlerTest extends Specification { when: handler.configurations - def result = handler.nonInstrumentedScriptClassPath + def result = handler.instrumentedScriptClassPath then: result == classpath @@ -122,14 +103,7 @@ class DefaultScriptHandlerTest extends Specification { 1 * depMgmtServices.configurationContainer >> configurationContainer 1 * depMgmtServices.dependencyHandler >> dependencyHandler 1 * configurationContainer.create('classpath') >> configuration - 1 * configuration.attributes >> attributes - 1 * attributes.attribute(Usage.USAGE_ATTRIBUTE, _ as Usage) - 1 * attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, _) - 1 * attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, _ as Bundling) - 1 * configuration.getDependencyConstraints() >> dependencyConstraintSet - 1 * dependencyConstraintSet.add(_) - 1 * dependencyHandler.getConstraints() >> dependencyConstraintHandler - 1 * dependencyConstraintHandler.create(_, _) + 1 * classpathResolver.prepareClassPath(configuration, dependencyHandler) 1 * classpathResolver.resolveClassPath(configuration) >> classpath } @@ -165,14 +139,7 @@ class DefaultScriptHandlerTest extends Specification { 1 * depMgmtServices.dependencyHandler >> dependencyHandler 1 * depMgmtServices.configurationContainer >> configurationContainer 1 * configurationContainer.create('classpath') >> configuration - 1 * configuration.attributes >> attributes - 1 * attributes.attribute(Usage.USAGE_ATTRIBUTE, _ as Usage) - 1 * attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, _ as Bundling) - 1 * attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, _) - 1 * configuration.getDependencyConstraints() >> dependencyConstraintSet - 1 * dependencyConstraintSet.add(_) - 1 * dependencyHandler.getConstraints() >> dependencyConstraintHandler - 1 * dependencyConstraintHandler.create(_, _) + 1 * classpathResolver.prepareClassPath(configuration, dependencyHandler) 1 * dependencyHandler.add('config', 'dep') } } diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectSpec.groovy index bd66b181ef13..0f32f814ec4b 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectSpec.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectSpec.groovy @@ -25,6 +25,7 @@ import org.gradle.api.file.ConfigurableFileTree import org.gradle.api.internal.GradleInternal import org.gradle.api.internal.file.FileOperations import org.gradle.api.internal.file.TestFiles +import org.gradle.api.internal.file.temp.DefaultTemporaryFileProvider import org.gradle.api.internal.initialization.ClassLoaderScope import org.gradle.api.internal.provider.DefaultPropertyFactory import org.gradle.api.internal.provider.PropertyHost @@ -162,7 +163,7 @@ class DefaultProjectSpec extends Specification { _ * serviceRegistry.get(DynamicLookupRoutine) >> Stub(DynamicLookupRoutine) def fileOperations = Stub(FileOperations) - fileOperations.fileTree(_) >> TestFiles.fileOperations(tmpDir.testDirectory).fileTree('tree') + fileOperations.fileTree(_) >> TestFiles.fileOperations(tmpDir.testDirectory, new DefaultTemporaryFileProvider(() -> new File(tmpDir.testDirectory, "cache"))).fileTree('tree') def projectDir = new File("project") def objectFactory = Stub(ObjectFactory) objectFactory.fileCollection() >> TestFiles.fileCollectionFactory().configurableFiles() diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTasks.java b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTasks.java index 25884e90b32c..1e8c866d06fe 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTasks.java +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTasks.java @@ -292,7 +292,6 @@ public TaskWithOutputFiles(List outputFiles) { this.outputFiles = outputFiles; } - @SuppressWarnings("deprecation") @OutputFiles public List getOutputFiles() { return outputFiles; @@ -300,7 +299,7 @@ public List getOutputFiles() { } public static class TaskWithBridgeMethod extends TaskWithAction implements WithProperty { - @org.gradle.api.tasks.Nested + @Nested private SpecificProperty nestedProperty = new SpecificProperty(); public int traversedOutputsCount; @@ -310,14 +309,14 @@ public SpecificProperty getNestedProperty() { } } - public interface WithProperty { + public interface WithProperty> { T getNestedProperty(); } public interface PropertyContainer {} public static class SpecificProperty extends SomePropertyContainer {} public static class SomeProperty {} - public static abstract class SomePropertyContainer implements PropertyContainer { + public static abstract class SomePropertyContainer implements PropertyContainer { @OutputFile public File getSomeOutputFile() { return new File("hello"); @@ -326,16 +325,16 @@ public File getSomeOutputFile() { public static class TaskWithOptionalOutputFile extends TaskWithAction { @OutputFile - @org.gradle.api.tasks.Optional + @Optional public File getOutputFile() { return null; } } public static class TaskWithOptionalOutputFiles extends TaskWithAction { - @SuppressWarnings("deprecation") + @OutputFiles - @org.gradle.api.tasks.Optional + @Optional public List getOutputFiles() { return null; } @@ -363,7 +362,6 @@ public TaskWithOutputDirs(List outputDirs) { this.outputDirs = outputDirs; } - @SuppressWarnings("deprecation") @OutputDirectories public List getOutputDirs() { return outputDirs; @@ -372,16 +370,16 @@ public List getOutputDirs() { public static class TaskWithOptionalOutputDir extends TaskWithAction { @OutputDirectory - @org.gradle.api.tasks.Optional + @Optional public File getOutputDir() { return null; } } public static class TaskWithOptionalOutputDirs extends TaskWithAction { - @SuppressWarnings("deprecation") + @OutputDirectories - @org.gradle.api.tasks.Optional + @Optional public File getOutputDirs() { return null; } @@ -421,7 +419,7 @@ public void doStuff() { public static class TaskWithOptionalInputFile extends TaskWithAction { @InputFile - @org.gradle.api.tasks.Optional + @Optional public File getInputFile() { return null; } @@ -541,7 +539,7 @@ public TaskWithOptionalNestedBean(Bean bean) { } @Nested - @org.gradle.api.tasks.Optional + @Optional public Bean getBean() { return bean; } @@ -551,7 +549,7 @@ public static class TaskWithOptionalNestedBeanWithPrivateType extends TaskWithAc Bean2 bean = new Bean2(); @Nested - @org.gradle.api.tasks.Optional + @Optional public Bean getBean() { return null; } diff --git a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy index 608164d719b4..522cfaf4e676 100644 --- a/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/configuration/DefaultProjectsPreparerTest.groovy @@ -15,13 +15,10 @@ */ package org.gradle.configuration - import org.gradle.api.internal.GradleInternal import org.gradle.api.internal.StartParameterInternal import org.gradle.api.internal.project.ProjectInternal import org.gradle.execution.ProjectConfigurer -import org.gradle.initialization.ModelConfigurationListener -import org.gradle.internal.build.BuildStateRegistry import org.gradle.internal.buildtree.BuildModelParameters import org.gradle.internal.operations.BuildOperationExecutor import spock.lang.Specification @@ -32,10 +29,8 @@ class DefaultProjectsPreparerTest extends Specification { def rootProject = Mock(ProjectInternal) def projectConfigurer = Mock(ProjectConfigurer) def modelParameters = Mock(BuildModelParameters) - def modelListener = Mock(ModelConfigurationListener) def buildOperationExecutor = Mock(BuildOperationExecutor) - def buildStateRegistry = Mock(BuildStateRegistry) - def configurer = new DefaultProjectsPreparer(projectConfigurer, modelParameters, modelListener, buildOperationExecutor, buildStateRegistry) + def configurer = new DefaultProjectsPreparer(projectConfigurer, modelParameters, buildOperationExecutor) def setup() { gradle.startParameter >> startParameter diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/selection/DefaultBuildTaskSelectorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/selection/DefaultBuildTaskSelectorTest.groovy index 5f51292ae42f..a0c005d05d5c 100644 --- a/subprojects/core/src/test/groovy/org/gradle/execution/selection/DefaultBuildTaskSelectorTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/execution/selection/DefaultBuildTaskSelectorTest.groovy @@ -33,6 +33,8 @@ import org.gradle.internal.build.RootBuildState import org.gradle.util.Path import spock.lang.Specification +import java.util.function.Consumer + class DefaultBuildTaskSelectorTest extends Specification { def buildRegistry = Mock(BuildStateRegistry) def taskSelector = Mock(TaskSelector) @@ -256,7 +258,12 @@ class DefaultBuildTaskSelectorTest extends Specification { } private void withIncludedBuilds(IncludedBuildFixture... builds) { - _ * buildRegistry.includedBuilds >> builds.collect { it.state } + _ * buildRegistry.visitBuilds(_) >> { Consumer visitor -> + visitor.accept(root.state) + for (final def build in builds) { + visitor.accept(build.state) + } + } } private RootBuildFixture rootBuild() { @@ -273,6 +280,7 @@ class DefaultBuildTaskSelectorTest extends Specification { defaultProject.owner >> defaultProjectState defaultProjectState.name >> "proj" defaultProjectState.displayName >> Describables.of("") + defaultProjectState.projectPath >> Path.path(":proj") defaultProjectState.identityPath >> Path.path(":proj") defaultProjectState.owner >> build defaultProjectState.childProjects >> [defaultProjectState].toSet() @@ -286,6 +294,7 @@ class DefaultBuildTaskSelectorTest extends Specification { projects.rootProject >> rootProjectState rootProjectState.name >> "root" rootProjectState.displayName >> Describables.of("") + rootProjectState.projectPath >> Path.ROOT rootProjectState.identityPath >> Path.ROOT rootProjectState.owner >> build rootProjectState.childProjects >> rootChildProjects @@ -300,7 +309,10 @@ class DefaultBuildTaskSelectorTest extends Specification { _ * build.name >> name _ * build.projects >> projects + _ * build.importableBuild >> true + _ * build.projectsLoaded >> true _ * projects.rootProject >> rootProject + _ * rootProject.projectPath >> Path.ROOT _ * rootProject.identityPath >> Path.path(":${name}") _ * rootProject.owner >> build @@ -309,6 +321,7 @@ class DefaultBuildTaskSelectorTest extends Specification { private ProjectState addProject(RootBuildFixture build, String name) { def projectState = Mock(ProjectState) + projectState.projectPath >> Path.path(":$name") projectState.identityPath >> Path.path(":$name") projectState.owner >> build.state diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy index 762d69d00b26..6b804381a3fa 100644 --- a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactoryTest.groovy @@ -24,7 +24,9 @@ import org.gradle.api.internal.component.BuildableJavaComponent import org.gradle.api.internal.component.ComponentRegistry import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectState +import org.gradle.internal.classpath.CachedClasspathTransformer import org.gradle.internal.service.ServiceRegistry +import org.gradle.util.TestUtil import spock.lang.Specification import java.util.function.Function @@ -52,20 +54,9 @@ class BuildSrcBuildListenerFactoryTest extends Specification { getRootProject() >> project } - def "configures task names"() { - def listener = new BuildSrcBuildListenerFactory().create() - component.getBuildTasks() >> ['barBuild'] - - when: - listener.onConfigure(gradle) - - then: - 1 * startParameter.setTaskNames(['barBuild']) - } - def "executes buildSrc configuration action after projects are loaded"() { def action = Mock(Action) - def listener = new BuildSrcBuildListenerFactory(action).create() + def listener = new BuildSrcBuildListenerFactory(action, TestUtil.objectInstantiator(), Stub(CachedClasspathTransformer)).create() when: listener.projectsLoaded(gradle) diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy index e93cc5224f57..53022cb9da81 100644 --- a/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/initialization/buildsrc/BuildSrcUpdateFactoryTest.groovy @@ -16,8 +16,8 @@ package org.gradle.initialization.buildsrc -import org.gradle.internal.classpath.CachedClasspathTransformer import org.gradle.internal.buildtree.BuildTreeLifecycleController +import org.gradle.internal.classpath.DefaultClassPath import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider import org.junit.Rule import spock.lang.Specification @@ -27,24 +27,20 @@ class BuildSrcUpdateFactoryTest extends Specification { @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider(getClass()) - def launcher = Stub(BuildTreeLifecycleController) + def controller = Stub(BuildTreeLifecycleController) def listener = Stub(BuildSrcBuildListenerFactory.Listener) def listenerFactory = Mock(BuildSrcBuildListenerFactory) - def cachedClasspathTransformer = Mock(CachedClasspathTransformer) - def factory = new BuildSrcUpdateFactory(launcher, listenerFactory, cachedClasspathTransformer) + def factory = new BuildSrcUpdateFactory(listenerFactory) def "creates classpath"() { - listener.getRuntimeClasspath() >> [new File("dummy")] + def classpath = DefaultClassPath.of(new File("dummy")) + listener.getRuntimeClasspath() >> classpath when: - def classpath = factory.create() + def result = factory.create(controller) then: 1 * listenerFactory.create() >> listener - 1 * cachedClasspathTransformer.transform(_, _) >> { arguments -> - arguments[0] - } - - classpath.asFiles == [new File("dummy")] + result == classpath } } diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/fingerprint/impl/DefaultFileCollectionSnapshotterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/fingerprint/impl/DefaultFileCollectionSnapshotterTest.groovy index 87ca1b1c2db8..4c2d4e00d86c 100644 --- a/subprojects/core/src/test/groovy/org/gradle/internal/fingerprint/impl/DefaultFileCollectionSnapshotterTest.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/internal/fingerprint/impl/DefaultFileCollectionSnapshotterTest.groovy @@ -164,7 +164,7 @@ class DefaultFileCollectionSnapshotterTest extends Specification { @Override File newTemporaryDirectory(String... path) { - return null + return tmpDir.createDir(path) } @Override diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java index 0aaa36175794..b06b5a44a76e 100644 --- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java +++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/file/TestFiles.java @@ -28,6 +28,7 @@ import org.gradle.api.internal.tasks.TaskDependencyFactory; import org.gradle.api.tasks.util.PatternSet; import org.gradle.api.tasks.util.internal.PatternSets; +import org.gradle.cache.internal.TestCaches; import org.gradle.internal.Factory; import org.gradle.internal.concurrent.DefaultExecutorFactory; import org.gradle.internal.file.Deleter; @@ -142,10 +143,10 @@ public static FileFactory fileFactory() { } public static FileOperations fileOperations(File basedDir) { - return fileOperations(basedDir, null); + return fileOperations(basedDir, new DefaultTemporaryFileProvider(() -> new File(basedDir, "tmp"))); } - public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFileProvider temporaryFileProvider) { + public static FileOperations fileOperations(File basedDir, TemporaryFileProvider temporaryFileProvider) { FileResolver fileResolver = resolver(basedDir); FileSystem fileSystem = fileSystem(); @@ -159,10 +160,8 @@ public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFi return new DefaultFileOperations( fileResolver, - temporaryFileProvider, - TestUtil.instantiatorFactory().inject(), + TestUtil.instantiatorFactory().inject(), directoryFileTreeFactory(), - streamHasher(), fileHasher(), resourceHandlerFactory, fileCollectionFactory(basedDir), @@ -172,7 +171,8 @@ public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFi deleter(), documentationRegistry(), taskDependencyFactory(), - providerFactory()); + providerFactory(), + TestCaches.decompressionCacheFactory(temporaryFileProvider.newTemporaryDirectory("cache-dir"))); } public static ApiTextResourceAdapter.Factory textResourceAdapterFactory(@Nullable TemporaryFileProvider temporaryFileProvider) { @@ -277,4 +277,5 @@ public static String systemSpecificAbsolutePath(String path) { public static TemporaryFileProvider tmpDirTemporaryFileProvider(File baseDir) { return new DefaultTemporaryFileProvider(() -> baseDir); } + } diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java new file mode 100644 index 000000000000..000bf6562306 --- /dev/null +++ b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.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.cache.internal; + +import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; + +import javax.annotation.Nonnull; +import java.io.File; + +/** + * Static util class for obtaining test doubles for a {@link DecompressionCache}. + */ +public abstract class TestCaches { + private TestCaches() {} + + public static DecompressionCache decompressionCache(File cacheDir) { + return decompressionCacheFactory(cacheDir).create(); + } + + public static DecompressionCacheFactory decompressionCacheFactory(File cacheDir) { + return new DecompressionCacheFactory() { + final TestInMemoryCacheFactory cacheFactory = new TestInMemoryCacheFactory(); + + @Nonnull + @Override + public DecompressionCache create() { + return new DefaultDecompressionCache(cacheFactory.open(cacheDir, "test compression cache")); + } + }; + } +} 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 c1109b7f9fa0..1df760b27b58 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 @@ -540,7 +540,7 @@ class RepositoriesDeclaredInSettingsIntegrationTest extends AbstractModuleDepend * the `buildSrc` directory behaves like an included build. As such, it may have its own settings, * so repositories declared in the main build shouldn't be visible to buildSrc. */ - def "repositories declared in settings shoudn't be used to resolve dependencies in buildSrc"() { + def "repositories declared in settings shouldn't be used to resolve dependencies in buildSrc"() { repository { 'org:module:1.0'() } @@ -559,7 +559,7 @@ class RepositoriesDeclaredInSettingsIntegrationTest extends AbstractModuleDepend fails ':help' then: - result.assertTaskExecuted(':buildSrc:pluginUnderTestMetadata') + result.assertTaskExecuted(':buildSrc:jar') result.assertTaskNotExecuted(':help') failure.assertHasCause('Cannot resolve external dependency org:module:1.0 because no repositories are defined.') } 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 eaa3e115c241..8bf83129ded7 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 @@ -19,7 +19,7 @@ package org.gradle.integtests.resolve.attributes import org.gradle.integtests.fixtures.AbstractIntegrationSpec class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { - def "matching attribute combinations and capabilities triggers a warning"() { + def "matching attribute combinations and capability #capability fails to resolve"() { given: buildFile << """ plugins { @@ -35,6 +35,9 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "custom")) } + if (!"${capability}".equals('default')) { + outgoing.capability('org.test:sample:1.0') + } } sample2 { @@ -45,25 +48,46 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "custom")) } + if (!"${capability}".equals('default')) { + outgoing.capability('org.test:sample:1.0') + } } - }""".stripIndent() + + resolver { + canBeResolved = true + canBeConsumed = false + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "custom")) + } + } + } + + dependencies { + resolver(project) + } + + tasks.register('resolveSample', Copy) { + from configurations.resolver + into layout.buildDirectory.dir('sampleContent') + } + """.stripIndent() expect: - executer.expectDeprecationWarning("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. This behavior has been deprecated.") - succeeds("outgoingVariants") + 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.") + + where: + capability << ['default', 'org.test:sample:1.0'] } - def "matching attribute combinations using the default capability, triggers a warning"() { + def "matching attribute combinations and capability #capability triggers a warning in outgoingVariants report"() { given: - settingsFile << "rootProject.name = 'sample'" buildFile << """ plugins { id 'java' } - group = 'org.gradle' - version = '1.0' - configurations { sample1 { canBeResolved = false @@ -73,6 +97,9 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "custom")) } + if (!"${capability}".equals('default')) { + outgoing.capability('org.test:sample:1.0') + } } sample2 { @@ -83,13 +110,18 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "custom")) } + if (!"${capability}".equals('default')) { + outgoing.capability('org.test:sample:1.0') + } } }""".stripIndent() expect: - executer.expectDeprecationWarning("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. This behavior has been deprecated.") succeeds("outgoingVariants") - outputContains("org.gradle:sample:1.0 (default capability)") + outputContains("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.") + + where: + capability << ['default', 'org.test:sample:1.0'] } def "matching attribute combinations, where one uses the default capability and one uses a matching explicit capability, triggers a warning"() { @@ -130,9 +162,9 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { } }""".stripIndent() - expect: - executer.expectDeprecationWarning("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. This behavior has been deprecated.") succeeds("outgoingVariants") + expect: + outputContains("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.") outputContains("org.gradle:sample:1.0 (default capability)") } @@ -175,6 +207,7 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { expect: succeeds("outgoingVariants") + outputDoesNotContain('Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes') } def "attribute combinations can be repeated if capabilities differ, including the default capability without a warning"() { @@ -216,6 +249,7 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { expect: succeeds("outgoingVariants") + outputDoesNotContain('Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes') } def "attribute and capability combinations can be repeated across projects without a warning"() { @@ -279,6 +313,7 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { expect: succeeds("allOutgoingVariants") + outputDoesNotContain('Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes') } def "attribute and capability combinations, including the default capability, can be repeated across projects without a warning"() { @@ -340,6 +375,7 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { expect: succeeds("allOutgoingVariants") + outputDoesNotContain('Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes') } def "attribute combinations and capabilities on resolvable configurations can match without a warning"() { @@ -371,6 +407,7 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { expect: succeeds("outgoingVariants") + outputDoesNotContain('Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes') } def "attribute combinations and capabilities on legacy resolvable configurations can match without a warning"() { @@ -406,6 +443,7 @@ class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec { expect: succeeds("outgoingVariants") + outputDoesNotContain('Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes') } } 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 667b9e776d4f..b0757b4e61ca 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 @@ -596,10 +596,10 @@ If the artifacts are trustworthy, you will need to update the gradle/verificatio fails "compileJava" then: - failure.assertHasDescription terse ? """Dependency verification failed for configuration ':buildSrc:runtimeClasspath' + failure.assertHasDescription terse ? """Dependency verification failed for configuration ':buildSrc:buildScriptClasspath' 2 artifacts failed verification: - bar-1.0.jar (org:bar:1.0) from repository maven - - bar-1.0.pom (org:bar:1.0) from repository maven""" : """Dependency verification failed for configuration ':buildSrc:runtimeClasspath': + - bar-1.0.pom (org:bar:1.0) from repository maven""" : """Dependency verification failed for configuration ':buildSrc:buildScriptClasspath': - On artifact bar-1.0.jar (org:bar:1.0) in repository 'maven': checksum is missing from verification metadata.""" where: 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 5e0337009929..2ee7787d2cda 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 @@ -16,6 +16,7 @@ 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.DependencyConstraint; import org.gradle.api.artifacts.ExcludeRule; @@ -89,6 +90,16 @@ enum InternalState { boolean isCanBeMutated(); + /** + * Locks the configuration for mutation + *

+ * Any invalid state at this point will be added to the returned list of exceptions. + * Handling these becomes the responsibility of the caller. + * + * @return a list of validation failures when not empty + */ + List preventFromFurtherMutationLenient(); + /** * Reports whether this configuration uses {@link org.gradle.api.Incubating Incubating} attributes types, such as {@link org.gradle.api.attributes.Category#VERIFICATION}. * @return 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 1d1e4d35adc5..98032cc0d8de 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 @@ -24,6 +24,7 @@ import org.gradle.api.Action; import org.gradle.api.Describable; import org.gradle.api.DomainObjectSet; +import org.gradle.api.GradleException; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.InvalidUserDataException; import org.gradle.api.Project; @@ -67,6 +68,7 @@ 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.ResolverResults; import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyConstraint; import org.gradle.api.internal.artifacts.dependencies.DependencyConstraintInternal; @@ -115,6 +117,7 @@ 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; import org.gradle.internal.logging.text.TreeFormatter; @@ -147,8 +150,8 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -1101,6 +1104,15 @@ public boolean isCanBeMutated() { @Override public void preventFromFurtherMutation() { + preventFromFurtherMutation(false); + } + + @Override + public List preventFromFurtherMutationLenient() { + return preventFromFurtherMutation(true); + } + + private List preventFromFurtherMutation(boolean lenient) { // TODO This should use the same `MutationValidator` infrastructure that we use for other mutation types if (canBeMutated) { if (beforeLocking != null) { @@ -1116,32 +1128,44 @@ public void preventFromFurtherMutation() { // We will only check unique attributes if this configuration is consumable, not resolvable, and has attributes itself if (mustHaveUniqueAttributes(this) && !this.getAttributes().isEmpty()) { - ensureUniqueAttributes(); + return ensureUniqueAttributes(lenient); } + } + return Collections.emptyList(); } - private void ensureUniqueAttributes() { + private List ensureUniqueAttributes(boolean lenient) { final Set all = (configurationsProvider != null) ? configurationsProvider.getAll() : null; if (all != null) { final Collection allCapabilities = allCapabilitiesIncludingDefault(this); - final Consumer warnIfDuplicate = otherConfiguration -> { - if (hasSameCapabilitiesAs(allCapabilities, otherConfiguration) && hasSameAttributesAs(otherConfiguration)) { - DeprecationLogger.deprecateBehaviour("Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes, but " + getDisplayName() + " and " + otherConfiguration.getDisplayName() + " contain identical attribute sets.") - .withAdvice("Consider adding an additional attribute to one of the configurations to disambiguate them. Run the 'outgoingVariants' task for more details.") - .willBecomeAnErrorInGradle8() - .withUpgradeGuideSection(7, "unique_attribute_sets") - .nagUser(); - } - }; - - all.stream() + final Predicate isDuplicate = otherConfiguration -> hasSameCapabilitiesAs(allCapabilities, otherConfiguration) && hasSameAttributesAs(otherConfiguration); + List collisions = all.stream() .filter(c -> c != this) .filter(this::mustHaveUniqueAttributes) .filter(c -> !c.isCanBeMutated()) - .forEach(warnIfDuplicate); + .filter(isDuplicate) + .map(ResolveContext::getDisplayName) + .collect(Collectors.toList()); + if (!collisions.isEmpty()) { + DocumentedFailure.Builder builder = DocumentedFailure.builder(); + String advice = "Consider adding an additional attribute to one of the configurations to disambiguate them."; + if (!lenient) { + advice += " Run the 'outgoingVariants' task for more details."; + } + GradleException gradleException = builder.withSummary("Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes, but " + getDisplayName() + " and " + collisions + " contain identical attribute sets.") + .withAdvice(advice) + .withUserManual("upgrading_version_7", "unique_attribute_sets") + .build(); + if (lenient) { + return Collections.singletonList(gradleException); + } else { + throw gradleException; + } + } } + return Collections.emptyList(); } /** diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/LazyPublishArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/LazyPublishArtifact.java index 693b8167ce1c..b24cbbb65368 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/LazyPublishArtifact.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/LazyPublishArtifact.java @@ -61,7 +61,7 @@ public LazyPublishArtifact(Provider provider, TaskDependencyFactory taskDepen // https://github.com/spring-projects/spring-boot/issues/29074 // DeprecationLogger.deprecateInternalApi("constructor LazyPublishArtifact(Provider)") // .replaceWith("constructor LazyPublishArtifact(Provider, FileResolver)") - // .willBeRemovedInGradle8() + // .willBeRemovedInGradle9() // .withUpgradeGuideSection(7, "lazypublishartifact_fileresolver") // .nagUser(); } @@ -76,7 +76,7 @@ public LazyPublishArtifact(Provider provider, String version, FileResolver fi // DeprecationLogger.deprecateInternalApi("constructor LazyPublishArtifact(Provider, FileResolver) or constructor LazyPublishArtifact(Provider, String, FileResolver)" // + " with a null FileResolver") // .replaceWith("a non-null FileResolver") - // .willBeRemovedInGradle8() + // .willBeRemovedInGradle9() // .withUpgradeGuideSection(7, "lazypublishartifact_fileresolver") // .nagUser(); this.provider = Providers.internal(provider); 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 43c4c66fd761..e620f0af04b8 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 @@ -72,7 +72,7 @@ public enum CacheLayout { .changedTo(96, "6.4-rc-1") .changedTo(97, "6.8-rc-1") .changedTo(99, "7.5-rc-1") - .changedTo(100, "8.0-rc-1") + .changedTo(100, "8.0-milestone-5") ), RESOURCES(ROOT, "resources", introducedIn("1.9-rc-1")), 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 e2e6d38a9fc7..205df67b0193 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 @@ -152,7 +152,6 @@ private static void validateResolutionStrategy(ResolutionStrategyInternal resolu private static void failOnDependencyLockingConflictingWith(String conflicting) { throw new InvalidUserCodeException("Resolution strategy has both dependency locking and " + conflicting + " enabled. You must choose between the two modes."); - } private DependencyGraphBuilder createDependencyGraphBuilder( 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 1e7942d64667..f9f3e4c9bf16 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 @@ -578,7 +578,7 @@ rootProject.name = 'root' configurations { compileOnly.deprecateForResolution('compileClasspath') compileOnly.deprecateForConsumption { builder -> - builder.willBecomeAnErrorInGradle8().withUpgradeGuideSection(8, "foo") + builder.willBecomeAnErrorInGradle9().withUpgradeGuideSection(8, "foo") } } dependencies { 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 db1d5c45ab2f..9c6ac33e6aa2 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 @@ -1087,7 +1087,7 @@ compileClasspath - Compile classpath for source set 'main'. configurations { compileOnly.deprecateForResolution("compileClasspath") compileOnly.deprecateForConsumption { builder -> - builder.willBecomeAnErrorInGradle8().withUpgradeGuideSection(8, "foo") + builder.willBecomeAnErrorInGradle9().withUpgradeGuideSection(8, "foo") } implementation.extendsFrom compileOnly } diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPluginConvention.java b/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPluginConvention.java index 0c7f75ca461d..75767b6817a5 100644 --- a/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPluginConvention.java +++ b/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPluginConvention.java @@ -24,7 +24,7 @@ /** * The conventional configuration for the `ProjectReportsPlugin`. * - * @deprecated Please configure the tasks directly. This class is scheduled for removal in Gradle 8.0. + * @deprecated Please configure the tasks directly. This class is scheduled for removal in Gradle 9.0. */ @Deprecated public abstract class ProjectReportsPluginConvention { diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ConfigurationReportModelFactory.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ConfigurationReportModelFactory.java index a8297323471b..c4d529884409 100644 --- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ConfigurationReportModelFactory.java +++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ConfigurationReportModelFactory.java @@ -16,6 +16,7 @@ package org.gradle.api.tasks.diagnostics.internal.configurations.model; +import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.artifacts.ConfigurationVariant; import org.gradle.api.artifacts.PublishArtifact; @@ -78,7 +79,7 @@ private ReportConfiguration getOrConvert(ConfigurationInternal configuration, Pr private ReportConfiguration convertConfiguration(ConfigurationInternal configuration, Project project, FileResolver fileResolver, List extendedConfigurations) { // Important to lock the config prior to extracting the attributes, as some attributes, such as TargetJvmVersion, are actually added by this locking process - configuration.preventFromFurtherMutation(); + List lenientErrors = configuration.preventFromFurtherMutationLenient(); final List attributes = configuration.getAttributes().keySet().stream() .map(a -> convertAttributeInContainer(a, configuration.getAttributes(), project.getDependencies().getAttributesSchema())) @@ -117,7 +118,8 @@ private ReportConfiguration convertConfiguration(ConfigurationInternal configura type = null; } - return new ReportConfiguration(configuration.getName(), configuration.getDescription(), type, attributes, capabilities, artifacts, variants, extendedConfigurations); + return new ReportConfiguration(configuration.getName(), configuration.getDescription(), type, new ArrayList<>(lenientErrors), + attributes, capabilities, artifacts, variants, extendedConfigurations); } private ReportArtifact convertPublishArtifact(PublishArtifact publishArtifact, FileResolver fileResolver) { diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ReportConfiguration.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ReportConfiguration.java index 3a74efb28c69..fe24a07d9ef1 100644 --- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ReportConfiguration.java +++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/model/ReportConfiguration.java @@ -17,6 +17,7 @@ package org.gradle.api.tasks.diagnostics.internal.configurations.model; import com.google.common.collect.ImmutableList; +import org.gradle.api.GradleException; import javax.annotation.Nullable; import java.util.List; @@ -30,6 +31,8 @@ public final class ReportConfiguration { private final String description; @Nullable private final Type type; + + private final List lenientErrors; private final ImmutableList attributes; private final ImmutableList capabilities; private final ImmutableList artifacts; @@ -37,6 +40,7 @@ public final class ReportConfiguration { private final ImmutableList extendedConfigurations; ReportConfiguration(String name, @Nullable String description, @Nullable Type type, + List lenientErrors, List attributes, List capabilities, List artifacts, @@ -45,6 +49,7 @@ public final class ReportConfiguration { this.name = name; this.description = description; this.type = type; + this.lenientErrors = ImmutableList.copyOf(lenientErrors); this.attributes = ImmutableList.copyOf(attributes); this.capabilities = ImmutableList.copyOf(capabilities); this.artifacts = ImmutableList.copyOf(artifacts); @@ -66,6 +71,10 @@ public Type getType() { return type; } + public List getLenientErrors() { + return lenientErrors; + } + public List getAttributes() { return attributes; } diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/renderer/ConsoleConfigurationReportRenderer.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/renderer/ConsoleConfigurationReportRenderer.java index 47a1e4a1fe29..9cf5b8c8b35b 100644 --- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/renderer/ConsoleConfigurationReportRenderer.java +++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/configurations/renderer/ConsoleConfigurationReportRenderer.java @@ -17,6 +17,7 @@ package org.gradle.api.tasks.diagnostics.internal.configurations.renderer; import org.apache.commons.lang.StringUtils; +import org.gradle.api.GradleException; import org.gradle.api.tasks.diagnostics.internal.configurations.model.ConfigurationReportModel; import org.gradle.api.tasks.diagnostics.internal.configurations.model.ReportArtifact; import org.gradle.api.tasks.diagnostics.internal.configurations.model.ReportAttribute; @@ -182,6 +183,8 @@ private void writeConfiguration(ReportConfiguration config) { writeConfigurationNameHeader(config, spec.getReportedTypeAlias()); writeDescription(config.getDescription()); + writeErrors(config.getLenientErrors()); + if (!config.getAttributes().isEmpty() || (spec.isIncludeCapabilities() && !config.getCapabilities().isEmpty()) || (spec.isIncludeArtifacts() && !config.getArtifacts().isEmpty()) || @@ -225,6 +228,13 @@ private void writeDescription(String description) { } } + private void writeErrors(List lenientErrors) { + if (!lenientErrors.isEmpty()) { + indent(false); + lenientErrors.forEach(ex -> output.style(StyledTextOutput.Style.Failure).println(ex.getMessage())); + } + } + private String buildIndicators(ReportConfiguration config) { String indicators = ""; if (config.isLegacy()) { diff --git a/subprojects/docs/README.md b/subprojects/docs/README.md index 1a8f4b6b82d3..cf5866cd3056 100644 --- a/subprojects/docs/README.md +++ b/subprojects/docs/README.md @@ -134,7 +134,9 @@ Let's break down this example to explain: 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. -### Code Samples +### Testing samples and snippets + +#### Code samples Samples and output belong under `src/samples` and are published beside the user manual. See the `org.gradle.samples` plugin. @@ -148,20 +150,27 @@ To run tests for a single sample, let's say from `samples/java/application`: ./gradlew :docs:docsTest --tests "org.gradle.docs.samples.DependencyManagementSnippetsTest.java-application*" ``` -To run tests for a single snippet: +#### Code snippets -Let's say you want to run the snippet found at `src/snippets/dependencyManagement/customizingResolution-consistentResolution`. +Snippets live under `src/snippets`. -then you can run the following command line: +As an example, you can run Kotlin and Groovy snippets tests from [`src/snippets/java/toolchain-task/`](src/snippets/java/toolchain-task) using: +``` +./gradlew :docs:docsTest --tests "*.snippet-java-toolchain-task_*" +``` +You can also filter the tests for a specific DSL like this: ``` - ./gradlew :docs:docsTest --tests "*.snippet-dependency-management-customizing-resolution-consistent-resolution*" +./gradlew :docs:docsTest --tests "*.snippet-java-toolchain-task_kotlin_*" ``` -which would run both Groovy and Kotlin tests. +#### Testing with configuration cache It is possible to run samples and snippets with the configuration cache enabled to ensure compatibility. -To do that set the Gradle property `enableConfigurationCacheForDocsTests=true` in the command line or in the `gradle.properties` file. +You can do that by setting the Gradle property `enableConfigurationCacheForDocsTests` in the command line or in the `gradle.properties` file. +``` +./gradlew :docs:docsTest --tests "*.snippet-java-toolchain-task_*" -PenableConfigurationCacheForDocsTests=true +``` ## Groovy DSL Reference diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.initialization.Settings.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.initialization.Settings.xml index 035be805a54c..23dfc3c2f3e4 100644 --- a/subprojects/docs/src/docs/dsl/org.gradle.api.initialization.Settings.xml +++ b/subprojects/docs/src/docs/dsl/org.gradle.api.initialization.Settings.xml @@ -28,6 +28,9 @@ startParameter + + caches +

@@ -56,6 +59,9 @@ project + + caches +
diff --git a/subprojects/docs/src/docs/release/notes.md b/subprojects/docs/src/docs/release/notes.md index f155595d1b4a..1951b7342832 100644 --- a/subprojects/docs/src/docs/release/notes.md +++ b/subprojects/docs/src/docs/release/notes.md @@ -251,12 +251,63 @@ In Java projects, these tools will use the same version of Java required by the ### IDE Integration +#### Run `buildSrc` tasks +#### Improvements for `buildSrc` +#### Improvements for `buildSrc` builds +This release includes several improvements for [`buildSrc`](userguide/organizing_gradle_projects#sec:build_sources) builds to make them behave similarly to an [included build](userguide/composite_builds#composite_build_intro). + +##### Run `buildSrc` tasks directly +It is now possible to run the tasks of `buildSrc` from the command-line, using the same syntax used for the tasks of included builds. +For example, you can use `gradle buildSrc:build` to run the `build` task in the `buildSrc` build. + +TODO - link to running included build tasks + +#### `buildSrc` can include other builds +The `buildSrc` build can now include other builds by declaring them in `buildSrc/settings.gradle.kts` or `buildSrc/settings.gradle`. +You can use `pluginsManagement { includeBuild(someDir) }` or `includeBuild(someDir)` in this settings script to make other builds available for `buildSrc` + +TODO - link to declaring included builds + +#### Tests for `buildSrc` are no longer automatically run +When Gradle builds the output of `buildSrc` it only runs the tasks that produce that output. It no longer runs the `build` task. +In particular, this means that the tests of `buildSrc` and its subprojects are not built and executed when they are not needed. + +TODO - you can run these tasks from the command-line or edit buildSrc to restore the old behaviour; link to upgrade guide + +#### Init scripts are applied to `buildSrc` +Init scripts specified on the command-line using `--init-script` are now applied to `buildSrc`, in addition to the main build and all included builds. + +#### Improved Gradle User Home Cache Cleanup +Previously, cleanup of the caches in Gradle User Home used fixed retention periods (30 days or 7 days depending on the cache). +These retention periods can now be configured via the [Settings](dsl/org.gradle.api.initialization.Settings.html) object in an init script in Gradle User Home. + +```groovy +beforeSettings { settings -> + settings.caches { + downloadedResources.removeUnusedEntriesAfterDays = 45 + } +} +``` + +Furthermore, it was previously only possible to partially disable cache cleanup via the `org.gradle.cache.cleanup` Gradle property in Gradle User Home. +Disabling cache cleanup now affects more caches under Gradle User Home and can also be configured via the [Settings](dsl/org.gradle.api.initialization.Settings.html) object in an init script in Gradle User Home. + +```groovy +beforeSettings { settings -> + settings.caches { + cleanup = Cleanup.DISABLED + } +} +``` + +See [Configuring cleanup of caches and distributions](userguide/directory_layout.html#dir:gradle_user_home:configure_cache_cleanup) for more information. + ## 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. 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 d95ef1a08ceb..12a5a2e741ae 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 @@ -246,9 +246,4 @@ include::sample[dir="samples/build-organization/composite-builds/hierarchical-mu Limitations of the current implementation include: * No support for included builds that have publications that don't mirror the project default configuration. See <<#included_build_substitution_limitations,Cases where composite builds won't work>>. -* Software model based native builds are not supported. (Binary dependencies are not yet supported for native builds). * Multiple composite builds may conflict when run in parallel, if more than one includes the same build. Gradle does not share the project lock of a shared composite build to between Gradle invocation to prevent concurrent execution. - -Improvements we have planned for upcoming releases include: - -* Making the implicit `buildSrc` project an included build. diff --git a/subprojects/docs/src/docs/userguide/compatibility.adoc b/subprojects/docs/src/docs/userguide/compatibility.adoc index a888eecd76d8..86e2bccc1e15 100644 --- a/subprojects/docs/src/docs/userguide/compatibility.adoc +++ b/subprojects/docs/src/docs/userguide/compatibility.adoc @@ -18,7 +18,8 @@ The sections below describe Gradle's compatibility with several integrations. Other versions not listed here may or may not work. == Java -A Java version between 8 and 19 is required to execute Gradle. Java 20 and later versions are not yet supported. +A Java version between 8 and 19 is required to execute Gradle. +Java 20 and later versions are not yet supported. Java 6 and 7 can still be used for <>. @@ -46,7 +47,8 @@ For older Gradle versions, please see the table below which Java version is supp [[kotlin]] == Kotlin -Gradle is tested with Kotlin 1.6.10 through 1.7.22. +Gradle is tested with Kotlin 1.6.10 through 1.8.0. +Beta and RC versions may or may not work. Gradle plugins written in Kotlin target Kotlin 1.5 for compatibility with Gradle and Kotlin DSL build scripts, even though the embedded Kotlin runtime is Kotlin 1.7. @@ -79,4 +81,5 @@ Gradle is tested with Groovy 1.5.8 through 4.0.0. 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 and 7.4. Alpha and beta versions may or may not work. +Gradle is tested with Android Gradle Plugin 7.3 and 7.4. +Alpha and beta versions may or may not work. 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 32bf410a46dd..cecca1cbca89 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 @@ -1,7 +1,7 @@ :metadata-file-spec: https://github.com/gradle/gradle/blob/master/subprojects/docs/src/docs/design/gradle-module-metadata-latest-specification.md [[understanding-variant-selection]] -= Understanding variant selection += Understanding variant selection In other dependency management engines, like Apache Maven™, dependencies and artifacts are bound to a component that is published at a particular GAV (group-artifact-version) coordinates. The set of dependencies for this component are always the same, regardless of which artifact may be used from the component. @@ -52,11 +52,11 @@ Not all configurations are variants because they may be used for declaring or re The consumer can define any number of attributes. Each attribute helps narrow the possible variants that can be selected. -Attribute values do not need to be exact matches. +Attribute values do not need to be exact matches. The variant can also define any number of attributes. The attributes should describe how the variant is intended to be used. -For example, Gradle uses an attribute named `org.gradle.usage` to describe with how a component is used by the consumer (for compilation, for runtime execution, etc). +For example, Gradle uses an attribute named `org.gradle.usage` to describe with how a component is used by the consumer (for compilation, for runtime execution, etc). It is not unusual for a variant to have more attributes than the consumer needs to provide to select it. [[sec:variant-aware-matching]] @@ -84,7 +84,7 @@ There are two exceptions to this rule that bypass variant aware resolution: == A simple example -Let's consider an example where a consumer is trying to use a library for compilation. +Let's consider an example where a consumer is trying to use a library for compilation. 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. @@ -93,7 +93,7 @@ The consumer wants to resolve a variant that matches: Second, the producer needs to expose the different variants of the component. -The producer component exposes 2 variants: +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` @@ -234,14 +234,15 @@ 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. -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). +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). [[sec:variant-visual]] == Visualizing variant information +[[outgoing_variants_report]] === Outgoing variants report -The report task `outgoingVariants` shows the list of variants available for selection by consumers of the project. It displays the capabilities, attributes and artifacts for each variant. +The report task `outgoingVariants` shows the list of variants available for selection by consumers of the project. It displays the capabilities, attributes and artifacts for each variant. This task is similar to the `dependencyInsight` <>. @@ -374,6 +375,14 @@ As they indicate, this is where the difference is made between what needs to be It also shows _secondary_ variants, which are exclusive to Gradle projects and not published. For example, the secondary variant `classes` from `apiElements` is what allows Gradle to skip the JAR creation when compiling against a <>. +==== Information about invalid consumable configurations + +A project cannot have multiple configurations with the same attributes and capabilities. +In that case, the project will fail to build. + +In order to be able to visualize such issues, the outgoing variant reports handle those errors in a lenient fashion. +This allows the report to display information about the issue. + === Resolvable configurations report Gradle also offers a complimentary report task called `resolvableConfigurations` that displays the _resolvable_ configurations of a project, which are those which can have dependencies added and be resolved. The report will list their attributes and any configurations that they extend. It will also list a summary of any attributes which will be affected by <> or <> during resolution. 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 3956373f71f6..5f75e110ec68 100644 --- a/subprojects/docs/src/docs/userguide/extending-gradle/build_services.adoc +++ b/subprojects/docs/src/docs/userguide/extending-gradle/build_services.adoc @@ -102,5 +102,29 @@ link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:usesService(or [[operation_listener]] == Receiving information about task execution -A build service can be used to receive events as tasks are executed. To do this, create and register a build service that implements link:{javadocPath}/org/gradle/tooling/events/OperationCompletionListener.html[OperationCompletionListener]. -Then, you can use the methods on the link:{javadocPath}/org/gradle/build/event/BuildEventsListenerRegistry.html[BuildEventsListenerRegistry] service to start receiving events. +A build service can be used to receive events as tasks are executed. To do this, create and register a build service that implements link:{javadocPath}/org/gradle/tooling/events/OperationCompletionListener.html[OperationCompletionListener]: + +.Build service implementing `OperationCompletionListener` +==== +[source.multi-language-sample,java] +.TaskEventsService.java +---- +include::{snippetsPath}/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsService.java[tags=build-service] +---- +==== +<1> Implement the `OperationCompletionListener` interface in addition to the `BuildService` interface. +<2> Check if the finish event is a link:{javadocPath}/org/gradle/tooling/events/task/TaskFinishEvent.html[TaskFinishEvent]. + +Then, in the plugin you can use the methods on the link:{javadocPath}/org/gradle/build/event/BuildEventsListenerRegistry.html[BuildEventsListenerRegistry] service to start receiving events: + +.Registering BuildService in the `BuildEventsListenerRegistry` +==== +[source.multi-language-sample,java] +.TaskEventsPlugin.java +---- +include::{snippetsPath}/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsPlugin.java[] +---- +==== +<1> Use <> to obtain an instance of the `BuildEventsListenerRegistry`. +<2> Register the build service as usual. +<3> Use the service `Provider` to subscribe the build service to build events. 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 ad9f9c5aa30a..fc3bd6581c6d 100644 --- a/subprojects/docs/src/docs/userguide/jvm/building_java_projects.adoc +++ b/subprojects/docs/src/docs/userguide/jvm/building_java_projects.adoc @@ -59,7 +59,7 @@ This isn't sufficient to build any non-trivial Java project — at the very leas [NOTE] ==== Although the properties in the example are optional, we recommend that you specify them in your projects. -The toolchain options protects against problems with the project being built with different Java versions. +Configuring the toolchain protects against problems with the project being built with different Java versions. The version string is important for tracking the progression of the project. The project version is also used in archive names by default. ==== @@ -240,45 +240,78 @@ That's also how you can change the verbosity of the compiler, disable debug outp === Targeting a specific Java version By default, Gradle will compile Java code to the language level of the JVM running Gradle. -With the usage of <>, you can break that link by making sure a given Java version, defined by the build, is used for compilation, execution and documentation. -It is however possible to override some compiler and execution options at the task level. +If you need to target a specific version of Java when compiling, Gradle provides multiple options: -Since version 9, the Java compiler can be configured to produce bytecode for an older Java version while making sure the code does not use any APIs from a more recent version. -Gradle now supports this link:{groovyDslPath}/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:release[release] flag on `CompileOptions` directly for Java compilation. -This option takes precedence over the properties described below. +1. Using <> is a preferred way to target a language version. + +A toolchain uniformly handles compilation, execution and Javadoc generation, and it can be configured on the project level. +2. Using link:{groovyDslPath}/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:release[`release`] property is possible starting from Java 10. + +Selecting a Java release makes sure that compilation is done with the configured language level and against the JDK APIs from that Java version. +3. Using `sourceCompatibility` and `targetCompatibility` properties. + +Although not generally advised, these options were historically used to configure the Java version during compilation. + +[[sec:compiling_with_toolchain]] +==== Using toolchains + +When Java code is compiled using a specific toolchain, the actual compilation is carried out by a compiler of the specified Java version. +The compiler provides access to the language features and JDK APIs for the requested Java language version. + +In the simplest case, the toolchain can be configured for a project using the `java` extension. +This way, not only compilation benefits from it, but also other tasks such as `test` and `javadoc` will also consistently use the same toolchain. -[WARNING] ==== -Due to a https://bugs.openjdk.java.net/browse/JDK-8139607[bug in Java 9] that was fixed in Java 10, Gradle cannot leverage the `release` flag when compiling with Java 9. +include::sample[dir="snippets/java/toolchain-basic/kotlin",files="build.gradle.kts[tags=toolchain]"] +include::sample[dir="snippets/java/toolchain-basic/groovy",files="build.gradle[tags=toolchain]"] ==== +You can learn more about this in the <> guide. + +[[sec:compiling_with_release]] +==== Using Java release version + +Setting the link:{groovyDslPath}/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:release[release] flag ensures the specified language level is used regardless of which compiler actually performs the compilation. +To use this feature, the compiler must support the requested release version. +It is possible to specify an earlier release version while compiling with a more recent <<#sec:compiling_with_toolchain,toolchain>>. + +Gradle supports using the release flag from Java 10. +It can be configured on the compilation task as follows. + .Setting Java release flag ==== include::sample[dir="snippets/java/basic/kotlin",files="build.gradle.kts[tags=java-release-flag]"] include::sample[dir="snippets/java/basic/groovy",files="build.gradle[tags=java-release-flag]"] ==== -Historical options for the Java compiler remain available: +The release flag provides guarantees similar to toolchains. +It validates that the Java sources are not using language features introduced in later Java versions, and also that the code does not access APIs from more recent JDKs. +The bytecode produced by the compiler also corresponds to the requested Java version, meaning that the compiled code cannot be executed on older JVMs. -`sourceCompatibility`:: -Defines which language version of Java your source files should be treated as. +The `release` option of the Java compiler was introduced in Java 9. +However, using this option with Gradle is only possible starting with Java 10, due to a https://bugs.openjdk.java.net/browse/JDK-8139607[bug in Java 9]. -`targetCompatibility`:: -Defines the minimum JVM version your code should run on, i.e. it determines the version of byte code the compiler generates. +==== Using Java compatibility options -These options can be set per link:{groovyDslPath}/org.gradle.api.tasks.compile.JavaCompile.html[JavaCompile] task, or on the `java { }` extension for all compile tasks, using properties with the same names. - -[NOTE] +[WARNING] ==== -Using a toolchain makes it illegal to configure the `sourceCompatibility` or `targetCompatibility` at the `java { }` extension level. +Using compatibility properties can lead to runtime failures when executing compiled code due to weaker guarantees they provide. +Instead, consider using <<#sec:compiling_with_toolchain,toolchains>> or the <<#sec:compiling_with_release,release>> flag. ==== +The `sourceCompatibility` and `targetCompatibility` options correspond to the Java compiler options `-source` and `-target`. +They are considered a legacy mechanism for targeting a specific Java version. However, these options do not protect against the use of APIs introduced in later Java versions. -==== Compiling and testing Java 6/7 +`sourceCompatibility`:: +Defines the language version of Java used in your source files. + +`targetCompatibility`:: +Defines the minimum JVM version your code should run on, i.e. it determines the version of the bytecode generated by the compiler. + +These options can be set per link:{groovyDslPath}/org.gradle.api.tasks.compile.JavaCompile.html[JavaCompile] task, or on the `java { }` extension for all compile tasks, using properties with the same names. -Gradle can only run on Java version 8 or higher. -Gradle still supports compiling, testing, generating Javadoc and executing applications for Java 6 and Java 7. +==== Targeting Java 6 and Java 7 + +Gradle itself can only run on a JVM with Java version 8 or higher. +However, Gradle still supports compiling, testing, generating Javadocs and executing applications for Java 6 and Java 7. Java 5 and below are not supported. [NOTE] @@ -294,8 +327,7 @@ To use Java 6 or Java 7, the following tasks need to be configured: With the usage of Java toolchains, this can be done as follows: -==== Example: Configure Java 7 build - +.Configuring Java 7 build ==== include::sample[dir="snippets/java/crossCompilation/kotlin",files="build.gradle.kts[tags=java-cross-compilation]"] include::sample[dir="snippets/java/crossCompilation/groovy",files="build.gradle[tags=java-cross-compilation]"] diff --git a/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc b/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc index ffc7ddb0d8a8..46bf4f17ee45 100644 --- a/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc +++ b/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc @@ -379,17 +379,28 @@ Runtime classpath contains elements of the implementation, as well as runtime on The Java plugin adds the link:{groovyDslPath}/org.gradle.api.plugins.JavaPluginExtension.html[`java` extension] to the project. This allows to configure a number of Java related properties inside a dedicated DSL block. -.Using the `java` extension +.Using the `java` extension to configure a toolchain ==== include::sample[dir="snippets/java/basic/kotlin",files="build.gradle.kts[tags=java-extension]"] include::sample[dir="snippets/java/basic/groovy",files="build.gradle[tags=java-extension]"] ==== +Below is the list of properties and DSL functions with short explanations available inside the `java` extension. + +=== Toolchain and compatibility + +`toolchain`:: +<> to be used by tasks using JVM tools, such as compilation and execution. Default value: build JVM toolchain. + `link:{javadocPath}/org/gradle/api/JavaVersion.html[JavaVersion] sourceCompatibility`:: -Java version compatibility to use when compiling Java source. Default value: version of the current JVM in use. +Java version compatibility to use when compiling Java source. Default value: language version of the toolchain from this extension. + +_Note that using a <> is preferred to using a compatibility setting for most cases._ `link:{javadocPath}/org/gradle/api/JavaVersion.html[JavaVersion] targetCompatibility`:: -Java version to generate classes for. Default value: `__sourceCompatibility__`. +Java version to generate classes for. Default value: `__sourceCompatibility__`. + +_Note that using a <> is preferred to using a compatibility setting for most cases._ + +=== Packaging `withJavadocJar()`:: Automatically packages Javadoc and creates a variant `javadocElements` with an artifact `-javadoc.jar`, which will be part of the publication. @@ -436,7 +447,7 @@ The name of the directory to generate documentation into, relative to the build The directory to generate documentation into. Default value: `__buildDir__/__docsDirName__` `String dependencyCacheDirName`:: -The name of the directory to use to cache source dependency information, relative to the build directory. Default value: `dependency-cache` +The name of the directory to cache source dependency information relative to the build directory. Default value: `dependency-cache`. === Other properties diff --git a/subprojects/docs/src/docs/userguide/jvm/scala_plugin.adoc b/subprojects/docs/src/docs/userguide/jvm/scala_plugin.adoc index 31be707d7f3c..db0ceb60a09d 100644 --- a/subprojects/docs/src/docs/userguide/jvm/scala_plugin.adoc +++ b/subprojects/docs/src/docs/userguide/jvm/scala_plugin.adoc @@ -16,8 +16,7 @@ = The Scala Plugin The Scala plugin extends the <> to add support for https://www.scala-lang.org/[Scala] projects. -It can deal with Scala code, mixed Scala and Java code, and even pure Java code (although we don't necessarily recommend to use it for the latter). -The plugin supports _joint compilation_, which allows you to freely mix and match Scala and Java code, with dependencies in both directions. +The plugin also supports _joint compilation_, which allows you to freely mix and match Scala and Java code with dependencies in both directions. For example, a Scala class can extend a Java class that in turn extends a Scala class. This makes it possible to use the best language for the job, and to rewrite any class in the other language if needed. @@ -60,7 +59,7 @@ Compiles the given source set's Scala source files. + Generates API documentation for the production Scala source files. -The `ScalaCompile` and `ScalaDoc` tasks can also leverage the <>. +The `ScalaCompile` and `ScalaDoc` tasks support <> out of the box. The Scala plugin adds the following dependencies to tasks added by the Java plugin. diff --git a/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc b/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc index cf328cd0e492..1b0680fb7e8c 100644 --- a/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc +++ b/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc @@ -15,31 +15,36 @@ [[toolchains]] = Toolchains for JVM projects -By default, Gradle uses the same Java version for running Gradle itself and building JVM projects. +Working on multiple projects can require interacting with multiple versions of the Java language. +Even within a single project different parts of the codebase may be fixed to a particular language level due to backward compatibility requirements. +This means different versions of the same tools (a toolchain) must be installed and managed on each machine that builds the project. -This is not always desirable. +A **Java toolchain** is a set of tools to build and run Java projects, which is usually provided by the environment via local JRE or JDK installations. +Compile tasks may use `javac` as their compiler, test and exec tasks may use the `java` command while `javadoc` will be used to generate documentation. + +By default, Gradle uses the same Java toolchain for running Gradle itself and building JVM projects. +However, this may only sometimes be desirable. Building projects with different Java versions on different developer machines and CI servers may lead to unexpected issues. Additionally, you may want to build a project using a Java version that is not supported for running Gradle. -A Java Toolchain (from now on referred to simply as toolchain) is a set of tools, usually taken from a local JRE/JDK installation that are used to configure different aspects of a build. -Compile tasks may use `javac` as their compiler, test and exec tasks may use the `java` command while `javadoc` will be used to generate documentation. +In order to improve reproducibility of the builds and make build requirements clearer, Gradle allows configuring toolchains on both project and task levels. [[sec:consuming]] -== Consuming Toolchains +== Toolchains for projects -A build can globally define what toolchain it targets by stating the Java Language version it needs and optionally the vendor: +You can define what toolchain to use for a project by stating the Java language version in the `java` extension block: ==== -include::sample[dir="samples/java/jvm-multi-project-with-toolchains/kotlin/",files="buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts[tags=toolchain]"] -include::sample[dir="samples/java/jvm-multi-project-with-toolchains/groovy/",files="buildSrc/src/main/groovy/myproject.java-conventions.gradle[tags=toolchain]"] +include::sample[dir="snippets/java/toolchain-basic/kotlin",files="build.gradle.kts[tags=toolchain]"] +include::sample[dir="snippets/java/toolchain-basic/groovy",files="build.gradle[tags=toolchain]"] ==== Executing the build (e.g. using `gradle check`) will now handle several things for you and others running your build: -1. Setup all compile, test and javadoc tasks to use the defined toolchain which may be different than the one Gradle itself uses -2. Gradle detects <<#sec:auto_detection,locally installed JVMs>> -3. Gradle chooses a JRE/JDK matching the requirements of the build (in this case a JVM supporting Java 11) -4. If no matching JVM is found, it will automatically download a matching JDK based on the <<#sub:download_repositories,toolchain download repositories>> configured. +1. Gradle configures all compile, test and javadoc tasks to use the defined toolchain. +2. Gradle detects <<#sec:auto_detection,locally installed toolchains>>. +3. Gradle chooses a toolchain matching the requirements (any Java 17 toolchain for the example above). +4. If no matching toolchain is found, Gradle can automatically download a matching one based on the configured <<#sub:download_repositories,toolchain download repositories>>. [NOTE] ==== @@ -49,7 +54,7 @@ For the Scala plugin, compilation and Scaladoc generation are supported. ==== [[sec:vendors]] -=== Using toolchains by specific vendors +=== Selecting toolchains by vendor In case your build has specific requirements from the used JRE/JDK, you may want to define the vendor for the toolchain as well. link:{javadocPath}/org/gradle/jvm/toolchain/JvmVendorSpec.html[`JvmVendorSpec`] has a list of well-known JVM vendors recognized by Gradle. @@ -71,7 +76,7 @@ include::sample[dir="snippets/java/toolchain-filters/kotlin/",files="build.gradl include::sample[dir="snippets/java/toolchain-filters/groovy/",files="build.gradle[tags=toolchain-matching-vendor]"] ==== -=== Selecting toolchains by their virtual machine implementation +=== Selecting toolchains by virtual machine implementation If your project requires a specific implementation, you can filter based on the implementation as well. Currently available implementations to choose from are: @@ -113,20 +118,20 @@ A specification without a language version, in most cases, would be treated as a Usage of _invalid_ instances of `JavaToolchainSpec` results in a build error since Gradle 8.0. -== Specify custom toolchains for individual tasks +== Toolchains for tasks In case you want to tweak which toolchain is used for a specific task, you can specify the exact tool a task is using. For example, the `Test` task exposes a `JavaLauncher` property that defines which java executable to use for launching the tests. -In the example below, we configure all java compilation tasks to use JDK8. -Additionally, we introduce a new `Test` task that is going to run our unit tests but using a JDK 14. +In the example below, we configure all java compilation tasks to use Java 8. +Additionally, we introduce a new `Test` task that will run our unit tests using a JDK 17. ==== include::sample[dir="samples/java/jvm-multi-project-with-toolchains/kotlin",files="list/build.gradle.kts[tags=customToolchain]"] include::sample[dir="samples/java/jvm-multi-project-with-toolchains/groovy/",files="list/build.gradle[tags=customToolchain]"] ==== -In addition, in the `application` subproject, we add another Java execution task to run our application with JDK 14. +In addition, in the `application` subproject, we add another Java execution task to run our application with JDK 17. ==== include::sample[dir="samples/java/jvm-multi-project-with-toolchains/kotlin",files="application/build.gradle.kts[tags=customExec]"] @@ -146,9 +151,9 @@ Three tools are available: === Integration with tasks relying on a Java executable or Java home -Any tasks that can be configured with a path to a Java executable, or a Java home location, can benefit from toolchains. +Any task that can be configured with a path to a Java executable, or a Java home location, can benefit from toolchains. -While you will not be able to wire a toolchain tool directly, they all have metadata that gives access to their full path or to the path of the Java installation they belong to. +While you will not be able to wire a toolchain tool directly, they all have the metadata that gives access to their full path or to the path of the Java installation they belong to. For example, you can configure the `java` executable for a task as follows: @@ -157,14 +162,14 @@ include::sample[dir="snippets/java/toolchain-config-task/kotlin/",files="build.g include::sample[dir="snippets/java/toolchain-config-task/groovy/",files="build.gradle[tags=java-executable]"] ==== -Another example, you can configure the _Java Home_ for a task as follows: +As another example, you can configure the _Java Home_ for a task as follows: ==== include::sample[dir="snippets/java/toolchain-config-task/kotlin/",files="build.gradle.kts[tags=java-home]"] include::sample[dir="snippets/java/toolchain-config-task/groovy/",files="build.gradle[tags=java-home]"] ==== -Yet another example, you can configure the Java _compiler_ executable for a task as follows: +If you require a path to a specific tool such as Java compiler, you can obtain it as follows: ==== include::sample[dir="snippets/java/toolchain-config-task/kotlin/",files="build.gradle.kts[tags=java-compiler]"] @@ -202,7 +207,7 @@ In order to disable auto-detection, you can use the `org.gradle.java.installatio * Or put `org.gradle.java.installations.auto-detect=false` into your `gradle.properties` file. [[sec:provisioning]] -== Auto-Provisioning +== 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 <>. @@ -314,19 +319,19 @@ In order to disable auto-provisioning, you can use the `org.gradle.java.installa * Or put `org.gradle.java.installations.auto-download=false` into a `gradle.properties` file. [[sec:custom_loc]] -== Custom Toolchain locations +== Custom toolchain locations If auto-detecting local toolchains is not sufficient or disabled, there are additional ways you can let Gradle know about installed toolchains. If your setup already provides environment variables pointing to installed JVMs, you can also let Gradle know about which environment variables to take into account. -Assuming the environment variables `JDK8` and `JRE14` point to valid java installations, the following instructs Gradle to resolve those environment variables and consider those installations when looking for a matching toolchain. +Assuming the environment variables `JDK8` and `JRE17` point to valid java installations, the following instructs Gradle to resolve those environment variables and consider those installations when looking for a matching toolchain. ---- -org.gradle.java.installations.fromEnv=JDK8,JRE14 +org.gradle.java.installations.fromEnv=JDK8,JRE17 ---- Additionally, you can provide a comma-separated list of paths to specific installations using the `org.gradle.java.installations.paths` property. -For example, using the following in your `gradle.properties` will let Gradle know which directories to look at when detecting JVMs. +For example, using the following in your `gradle.properties` will let Gradle know which directories to look at when detecting toolchains. Gradle will treat these directories as possible installations but will not descend into any nested directories. ---- @@ -337,11 +342,11 @@ org.gradle.java.installations.paths=/custom/path/jdk1.8,/shared/jre11 ==== Gradle does not prioritize custom toolchains over <> toolchains. If you enable auto-detection in your build, custom toolchains extend the set of toolchain locations. -Gradle picks a toolchain according to the <>. +Gradle picks a toolchain according to the <>. ==== [[sec:precedence]] -== Precedence +== Toolchain installations precedence Gradle will sort all the JDK/JRE installations matching the toolchain specification of the build and will pick the first one. Sorting is done based on the following rules: @@ -399,17 +404,37 @@ So Gradle picks the first match in this order: Microsoft JDK 17.0.1. [[sec:plugins]] == Toolchains for plugin authors -Custom tasks that require a tool from the JDK should expose a `Property` with the desired tool as generic type. -The property should be declared as a <>. -By injecting the `JavaToolchainService` in the plugin or task, it is also possible to wire a convention in those properties by obtaining the `JavaToolchainSpec` from the `java` extension on the project. +When creating a plugin or a task that uses toolchains, it is essential to provide sensible defaults and allow users to override them. + +For JVM projects, it is usually safe to assume that the `java` plugin has been applied to the project. +The `java` plugin is automatically applied for the core Groovy and Scala plugins, as well as for the Kotlin plugin. +In such a case, using the toolchain defined via the `java` extension as a default value for the tool property is appropriate. +This way, the users will need to configure the toolchain only once on the project level. + The example below showcases how to use the default toolchain as convention while allowing users to individually configure the toolchain per task. ==== -include::sample[dir="snippets/java/toolchain-task/kotlin/",files="build.gradle.kts"] -include::sample[dir="snippets/java/toolchain-task/groovy/",files="build.gradle"] +include::sample[dir="snippets/java/toolchain-task/kotlin/",files="build.gradle.kts[tags=custom-toolchain-task-with-java]"] +include::sample[dir="snippets/java/toolchain-task/groovy/",files="build.gradle[tags=custom-toolchain-task-with-java]"] ==== +<1> We declare a `JavaLauncher` property on the task. +The property must be marked as a <> to make sure the task is responsive to toolchain changes. +<2> We obtain the toolchain spec from the `java` extension to use it as a default. +<3> Using the `JavaToolchainService` we get a provider of the `JavaLauncher` that matches the toolchain. +<4> Finally, we wire the launcher provider as a convention for our property. + +In a project where the `java` plugin was applied, we can use the task as follows: -[NOTE] ==== -With the property correctly configured as `@Nested`, it will automatically track the Java major version, the vendor (if specified) and implementation (if specified) as an input. +include::sample[dir="snippets/java/toolchain-task/kotlin/",files="build.gradle.kts[tags=custom-toolchain-task-with-java-usage]"] +include::sample[dir="snippets/java/toolchain-task/groovy/",files="build.gradle[tags=custom-toolchain-task-with-java-usage]"] ==== +<1> The toolchain defined on the `java` extension is used by default to resolve the launcher. +<2> The custom task without additional configuration will use the default Java 8 toolchain. +<3> The other task overrides the value of the launcher by selecting a different toolchain using `javaToolchains` service. + +When a task needs access to toolchains without the `java` plugin being applied the toolchain service can be used directly. +If an <<#sec:configuring_toolchain_specifications, unconfigured>> toolchain spec is provided to the service, it will always return a tool provider for the toolchain that is running Gradle. +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 <> documentation. 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 3eeb801136dc..88db058c2378 100644 --- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_6.adoc +++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_6.adoc @@ -34,6 +34,11 @@ Some plugins will break with this new version of Gradle, for example because the . Try to run the project and debug any errors using the <>. [[changes_7.0]] +== Upgrading from 6.9 + +Nothing to do. + +[[changes_6.9]] == Upgrading from 6.8 === Changes in the IDE integration 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 1d6f8c8fa0c3..0627e82c8bad 100644 --- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc +++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc @@ -328,7 +328,7 @@ android.experimental.legacyTransform.forceNonIncremental=true Previously, `org.gradle.api.AntBuilder` extended the deprecated `groovy.util.AntBuilder` class. It now extends `groovy.ant.AntBuilder`. -==== PluginDeclaration is not serializable +==== `PluginDeclaration` is not serializable `org.gradle.plugin.devel.PluginDeclaration` is not serializable anymore. If you need to serialize it, you can convert it into your own, serializable class. @@ -383,6 +383,22 @@ Starting in Gradle 7.6, builds could enable this behavior with the `org.gradle.k ==== Adding `jst.ejb` with the `eclipse wtp` plugin now removes the `jst.utility` facet +==== Init scripts are applied to `buildSrc` builds + +Init script specified using `--init-script` are now applied to `buildSrc` builds. In previous releases these were applied to included builds but not `buildSrc builds. +This behaviour is now consistent for `buildSrc` and included builds. + +==== Gradle no longer runs the `build` task for `buildSrc` builds + +TODO - only the `jar` task and its dependencies are run, same as for included builds. Can run `buildSrc` tasks from the command-line, if required. If `buildSrc` contains +subprojects that are not dependencies of `buildSrc`, these are not automatically built. + +==== `buildFinished { }` hook for `buildSrc` runs after all tasks have executed + +TODO - hook runs at the same time as for included builds + +==== Adding `jst.ejb` with the `eclipse wtp' plugin now removes the `jst.utility` facet + The `eclipse wtp` plugin adds the `jst.utility` facet to java projects. Now, adding the `jst.ejb` facet implicitly removes the `jst.utility` facet: @@ -517,6 +533,21 @@ The following configurations were never meant to be consumed: These configurations were deprecated for consumption and are now no longer consumable. Attempting to consume them will result in an error. +==== Identical consumable configurations are now an error + +If a project has multiple consumable configurations that share the same attributes and capabilities declaration, the build will fail when publishing or resolving as a dependency that project. +This was <<#unique_attribute_sets,previously deprecated>>. + +The <> will warn about this for impacted configurations. + +==== Toolchain-based tasks for JVM projects + +Starting with Gradle 8.0, all core Java tasks that have toolchain support are now using toolchains unconditionally. +If `JavaBasePlugin` is applied, the convention value for tool properties on the task is defined by the toolchain configured on the `java` extension. +In case no toolchains are explicitly configured, the toolchain corresponding to the JVM running Gradle is used. + +Similarly, tasks from the Groovy and Scala plugins also rely on toolchains to determine on which JVM they are executed. + [[changes_7.6]] == Upgrading from 7.5 and earlier @@ -1068,7 +1099,8 @@ This way, Gradle is able to apply optimizations like up-to-date checks instead o [[unique_attribute_sets]] ==== Unique attribute sets -The set of link:{javadocPath}/org/gradle/api/attributes/Attribute.html[Attribute]s associated with a _consumable_ configuration within a project, must be unique across all other configurations within that project which share the same set of link:{javadocPath}/org/gradle/api/capabilities/Capability.html[Capability]s. This will be checked at the end of configuring variant configurations, as they are locked against further mutation. +The set of link:{javadocPath}/org/gradle/api/attributes/Attribute.html[Attribute]s associated with a _consumable_ configuration within a project, must be unique across all other configurations within that project which share the same set of link:{javadocPath}/org/gradle/api/capabilities/Capability.html[Capability]s. +This will be checked at the end of configuring variant configurations, as they are locked against further mutation. If the set of attributes is shared across configurations, consider adding an additional attribute to one of the variants for the sole purpose of disambiguation. diff --git a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc index a50b699d461c..5cb1039a1665 100644 --- a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc +++ b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc @@ -62,17 +62,48 @@ It is roughly structured as follows: [[dir:gradle_user_home:cache_cleanup]] === Cleanup of caches and distributions -From version 4.10 onwards, Gradle automatically cleans its user home directory. -The cleanup runs in the background when the Gradle daemon is stopped or shuts down. +Gradle automatically cleans its user home directory. +By default, the cleanup runs in the background when the Gradle daemon is stopped or shuts down. If using `--no-daemon`, it runs in the foreground after the build session with a visual progress indicator. -The following cleanup strategies are applied periodically (at most every 24 hours): +The following cleanup strategies are applied periodically (by default, once every 24 hours): -- Version-specific caches in `caches//` are checked for whether they are still in use.If not, directories for release versions are deleted after 30 days of inactivity, snapshot versions after 7 days of inactivity. -- Shared caches in `caches/` (e.g. `jars-*`) are checked for whether they are still in use.If there's no Gradle version that still uses them, they are deleted. -- Files in shared caches used by the current Gradle version in `caches/` (e.g. `jars-3` or `modules-2`) are checked for when they were last accessed.Depending on whether the file can be recreated locally or would have to be downloaded from a remote repository again, it will be deleted after 7 or 30 days of not being accessed, respectively. -- Gradle distributions in `wrapper/dists/` are checked for whether they are still in use, i.e. whether there's a corresponding version-specific cache directory.Unused distributions are deleted. +- Version-specific caches in `caches//` are checked for whether they are still in use. If not, directories for release versions are deleted after 30 days of inactivity, snapshot versions after 7 days of inactivity. +- Shared caches in `caches/` (e.g. `jars-*`) are checked for whether they are still in use. If there's no Gradle version that still uses them, they are deleted. +- Files in shared caches used by the current Gradle version in `caches/` (e.g. `jars-3` or `modules-2`) are checked for when they were last accessed. Depending on whether the file can be recreated locally or would have to be downloaded from a remote repository again, it will be deleted after 7 or 30 days of not being accessed, respectively. +- Gradle distributions in `wrapper/dists/` are checked for whether they are still in use, i.e. whether there's a corresponding version-specific cache directory. Unused distributions are deleted. +[[dir:gradle_user_home:configure_cache_cleanup]] +==== Configuring cleanup of caches and distributions + +The retention periods of the various caches can be configured. Caches are classified into four categories: + +- *Released wrapper distributions:* Distributions and related version-specific caches that correspond to released versions (e.g. `4.6.2` or `8.0`). Default retention for unused versions is 30 days. +- *Snapshot wrapper distributions:* Distributions and related version-specific caches that correspond to snapshot versions (e.g. `7.6-20221130141522+0000`). Default retention for unused versions is 7 days. +- *Downloaded resources:* Shared caches that are downloaded from a remote repository (e.g. cached dependencies). Default retention for unused resources is 30 days. +- *Created resources:* Shared caches that are created by Gradle during the course of a build (e.g. artifact transforms). Default retention for unused resources is 7 days. + +The retention period for each category can be configured independently via an init script in Gradle User Home: + +.Configuring cache retention +==== +include::sample[dir="snippets/initScripts/cacheRetention/groovy",files="gradleUserHome/init.d/cache-settings.gradle"] +include::sample[dir="snippets/initScripts/cacheRetention/kotlin",files="gradleUserHome/init.d/cache-settings.gradle.kts"] +==== + +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. +- *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 +==== +include::sample[dir="snippets/initScripts/disableCacheCleanup/groovy",files="gradleUserHome/init.d/cache-settings.gradle"] +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. [[dir:project_root]] == Project root directory diff --git a/subprojects/docs/src/main/resources/header.html b/subprojects/docs/src/main/resources/header.html index e9798835f897..9037601bffa8 100644 --- a/subprojects/docs/src/main/resources/header.html +++ b/subprojects/docs/src/main/resources/header.html @@ -99,7 +99,7 @@

What is new?

  • Release Notes
    • -
    • version 7.X to 8.0
    • +
    • version 7.X to latest
    • version 6.X to 7.0
    • version 5.X to 6.0
    • version 4.X to 5.0
    • @@ -218,7 +218,7 @@

      Authoring JVM Builds

    • Testing Java & JVM projects
    • 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 9db0b1aea4ac..c278654fb851 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 @@ -6,7 +6,7 @@ group = "com.example.platform" dependencies { constraints { - api("com.android.tools.build:gradle:7.3.0") + api("com.android.tools.build:gradle:7.3.1") api("org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.7.22") api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.7.22") api("org.springframework.boot:org.springframework.boot.gradle.plugin:2.7.5") diff --git a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out index 85979de7cbf3..96f8c00946c4 100644 --- a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out +++ b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-distribution/tests/testTask.out b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-distribution/tests/testTask.out index fb1f214985fa..d55c9f64c8ba 100644 --- a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-distribution/tests/testTask.out +++ b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-distribution/tests/testTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-standalone/tests/testTask.out b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-standalone/tests/testTask.out index fd02b1a9e74f..c459b7ab5cd8 100644 --- a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-standalone/tests/testTask.out +++ b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-code-coverage-standalone/tests/testTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-distribution/tests/testTask.out b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-distribution/tests/testTask.out index 51f4c943cf15..5bca9515d65f 100644 --- a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-distribution/tests/testTask.out +++ b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-distribution/tests/testTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:processResources +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-standalone/tests/testTask.out b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-standalone/tests/testTask.out index 4a7951bf56c4..6fea16d71d30 100644 --- a/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-standalone/tests/testTask.out +++ b/subprojects/docs/src/samples/incubating/java/jvm-multi-project-with-test-aggregation-standalone/tests/testTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/checkTask.out b/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/checkTask.out index 357ba2c11c6a..a0ae0bc7e651 100644 --- a/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/checkTask.out +++ b/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/checkTask.out @@ -1,12 +1,6 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :list:processResources NO-SOURCE > Task :list:classes diff --git a/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/runTask.out b/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/runTask.out index 3959011f7392..a0f0e264a586 100644 --- a/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/runTask.out +++ b/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/runTask.out @@ -1,12 +1,4 @@ -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:pluginUnderTestMetadata UP-TO-DATE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins UP-TO-DATE -> Task :buildSrc:check UP-TO-DATE -> Task :buildSrc:build UP-TO-DATE +> Task :buildSrc:jar UP-TO-DATE > Task :list:compileJava UP-TO-DATE > Task :list:processResources NO-SOURCE > Task :list:classes UP-TO-DATE diff --git a/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf b/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf index 95ebd930296c..41c1e61702f3 100644 --- a/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf +++ b/subprojects/docs/src/samples/incubating/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf @@ -9,4 +9,5 @@ commands: [{ args: run expected-output-file: runTask.out allow-additional-output: true + allow-disordered-output: true }] diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out b/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out index 3b2e96766340..bda95418221b 100644 --- a/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out +++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/tests/checkTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/application/build.gradle b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/application/build.gradle index 606329be3b68..c411fa601afc 100644 --- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/application/build.gradle +++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/application/build.gradle @@ -13,9 +13,9 @@ application { } // tag::customExec[] -task('runOn14', type: JavaExec) { +task('runOn17', type: JavaExec) { javaLauncher = javaToolchains.launcherFor { - languageVersion = JavaLanguageVersion.of(14) + languageVersion = JavaLanguageVersion.of(17) } classpath = sourceSets.main.runtimeClasspath diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/list/build.gradle b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/list/build.gradle index 44810aa3f737..69509e949930 100644 --- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/list/build.gradle +++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/list/build.gradle @@ -9,9 +9,10 @@ tasks.withType(JavaCompile).configureEach { languageVersion = JavaLanguageVersion.of(8) } } -task('testsOn14', type: Test) { + +task('testsOn17', type: Test) { javaLauncher = javaToolchains.launcherFor { - languageVersion = JavaLanguageVersion.of(14) + languageVersion = JavaLanguageVersion.of(17) } } // end::customToolchain[] diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/application/build.gradle.kts b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/application/build.gradle.kts index bb9fa622af50..c18c40b871ad 100644 --- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/application/build.gradle.kts +++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/application/build.gradle.kts @@ -13,9 +13,9 @@ application { } // tag::customExec[] -tasks.register("runOn14") { +tasks.register("runOn17") { javaLauncher.set(javaToolchains.launcherFor { - languageVersion.set(JavaLanguageVersion.of(14)) + languageVersion.set(JavaLanguageVersion.of(17)) }) classpath = sourceSets["main"].runtimeClasspath diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/list/build.gradle.kts b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/list/build.gradle.kts index b6b74d4fd5e9..5ad69f4040c7 100644 --- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/list/build.gradle.kts +++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/list/build.gradle.kts @@ -10,9 +10,9 @@ tasks.withType().configureEach { }) } -tasks.register("testsOn14") { +tasks.register("testsOn17") { javaLauncher.set(javaToolchains.launcherFor { - languageVersion.set(JavaLanguageVersion.of(14)) + languageVersion.set(JavaLanguageVersion.of(17)) }) } // end::customToolchain[] diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/tests/checkTask.out b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/tests/checkTask.out index c73bfbfc1e6e..62524dfcb5e6 100644 --- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/tests/checkTask.out +++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/tests/checkTask.out @@ -1,12 +1,7 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:pluginDescriptors +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :utilities:compileJava > Task :application:compileJava diff --git a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/checkTask.out b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/checkTask.out index 00149672f5aa..142242222c43 100644 --- a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/checkTask.out +++ b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/checkTask.out @@ -1,12 +1,6 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :list:processResources NO-SOURCE > Task :list:classes diff --git a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/runTask.out b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/runTask.out index 3959011f7392..a0f0e264a586 100644 --- a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/runTask.out +++ b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/runTask.out @@ -1,12 +1,4 @@ -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:pluginUnderTestMetadata UP-TO-DATE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins UP-TO-DATE -> Task :buildSrc:check UP-TO-DATE -> Task :buildSrc:build UP-TO-DATE +> Task :buildSrc:jar UP-TO-DATE > Task :list:compileJava UP-TO-DATE > Task :list:processResources NO-SOURCE > Task :list:classes UP-TO-DATE diff --git a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf index 95ebd930296c..41c1e61702f3 100644 --- a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf +++ b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/tests/testTask.sample.conf @@ -9,4 +9,5 @@ commands: [{ args: run expected-output-file: runTask.out allow-additional-output: true + allow-disordered-output: true }] diff --git a/subprojects/docs/src/samples/java/modules-multi-project/tests/checkTask.out b/subprojects/docs/src/samples/java/modules-multi-project/tests/checkTask.out index 54b3dac6f4d4..3ea9a5471962 100644 --- a/subprojects/docs/src/samples/java/modules-multi-project/tests/checkTask.out +++ b/subprojects/docs/src/samples/java/modules-multi-project/tests/checkTask.out @@ -1,12 +1,6 @@ -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build +> Task :buildSrc:processResources +> Task :buildSrc:classes +> Task :buildSrc:jar > Task :list:compileJava > Task :list:processResources NO-SOURCE > Task :list:classes diff --git a/subprojects/docs/src/samples/java/modules-multi-project/tests/runTask.out b/subprojects/docs/src/samples/java/modules-multi-project/tests/runTask.out index 1b9a4e82411a..cb6df0db3aa6 100644 --- a/subprojects/docs/src/samples/java/modules-multi-project/tests/runTask.out +++ b/subprojects/docs/src/samples/java/modules-multi-project/tests/runTask.out @@ -1,12 +1,4 @@ -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:pluginUnderTestMetadata UP-TO-DATE -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins UP-TO-DATE -> Task :buildSrc:check UP-TO-DATE -> Task :buildSrc:build UP-TO-DATE +> Task :buildSrc:jar UP-TO-DATE > Task :list:compileJava UP-TO-DATE > Task :list:processResources NO-SOURCE > Task :list:classes UP-TO-DATE diff --git a/subprojects/docs/src/samples/java/modules-multi-project/tests/testTask.sample.conf b/subprojects/docs/src/samples/java/modules-multi-project/tests/testTask.sample.conf index 95ebd930296c..41c1e61702f3 100644 --- a/subprojects/docs/src/samples/java/modules-multi-project/tests/testTask.sample.conf +++ b/subprojects/docs/src/samples/java/modules-multi-project/tests/testTask.sample.conf @@ -9,4 +9,5 @@ commands: [{ args: run expected-output-file: runTask.out allow-additional-output: true + allow-disordered-output: true }] diff --git a/subprojects/docs/src/snippets/developingPlugins/conventionOverConfiguration/tests/deploy.out b/subprojects/docs/src/snippets/developingPlugins/conventionOverConfiguration/tests/deploy.out index cb9c7335af85..08e72a456f85 100644 --- a/subprojects/docs/src/snippets/developingPlugins/conventionOverConfiguration/tests/deploy.out +++ b/subprojects/docs/src/snippets/developingPlugins/conventionOverConfiguration/tests/deploy.out @@ -4,19 +4,9 @@ > Task :buildSrc:processResources > Task :buildSrc:classes > Task :buildSrc:jar -> Task :buildSrc:assemble -> Task :buildSrc:compileTestJava NO-SOURCE -> Task :buildSrc:compileTestGroovy NO-SOURCE -> Task :buildSrc:pluginUnderTestMetadata -> Task :buildSrc:processTestResources NO-SOURCE -> Task :buildSrc:testClasses UP-TO-DATE -> Task :buildSrc:test NO-SOURCE -> Task :buildSrc:validatePlugins -> Task :buildSrc:check -> Task :buildSrc:build > Task :deploy Deploying to URL http://localhost:8080/server BUILD SUCCESSFUL in 0s -7 actionable tasks: 7 executed +5 actionable tasks: 5 executed diff --git a/subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/build.gradle b/subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/build.gradle new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/gradleUserHome/init.d/cache-settings.gradle b/subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/gradleUserHome/init.d/cache-settings.gradle new file mode 100644 index 000000000000..c17c7b236c75 --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/gradleUserHome/init.d/cache-settings.gradle @@ -0,0 +1,8 @@ +beforeSettings { settings -> + settings.caches { + releasedWrappers.removeUnusedEntriesAfterDays = 45 + snapshotWrappers.removeUnusedEntriesAfterDays = 10 + downloadedResources.removeUnusedEntriesAfterDays = 45 + createdResources.removeUnusedEntriesAfterDays = 10 + } +} diff --git a/subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/build.gradle.kts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts b/subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts new file mode 100644 index 000000000000..9ffceef554bc --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts @@ -0,0 +1,8 @@ +beforeSettings { + caches { + releasedWrappers.setRemoveUnusedEntriesAfterDays(45) + snapshotWrappers.setRemoveUnusedEntriesAfterDays(10) + downloadedResources.setRemoveUnusedEntriesAfterDays(45) + createdResources.setRemoveUnusedEntriesAfterDays(10) + } +} diff --git a/subprojects/docs/src/snippets/initScripts/cacheRetention/tests-groovy/cacheRetention.sample.conf b/subprojects/docs/src/snippets/initScripts/cacheRetention/tests-groovy/cacheRetention.sample.conf new file mode 100644 index 000000000000..58b48f330f0f --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/cacheRetention/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/cacheRetention/tests-kotlin/cacheRetention.sample.conf b/subprojects/docs/src/snippets/initScripts/cacheRetention/tests-kotlin/cacheRetention.sample.conf new file mode 100644 index 000000000000..b2dfb8ff3006 --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/cacheRetention/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/initScripts/disableCacheCleanup/groovy/build.gradle b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/groovy/build.gradle new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/groovy/gradleUserHome/init.d/cache-settings.gradle b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/groovy/gradleUserHome/init.d/cache-settings.gradle new file mode 100644 index 000000000000..0ba5193ec463 --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/groovy/gradleUserHome/init.d/cache-settings.gradle @@ -0,0 +1,5 @@ +beforeSettings { settings -> + settings.caches { + cleanup = Cleanup.DISABLED + } +} diff --git a/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/build.gradle.kts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts new file mode 100644 index 000000000000..f2efe0dae7dd --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts @@ -0,0 +1,5 @@ +beforeSettings { + caches { + cleanup.set(Cleanup.DISABLED) + } +} diff --git a/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/tests-groovy/cacheRetention.sample.conf b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/tests-groovy/cacheRetention.sample.conf new file mode 100644 index 000000000000..58b48f330f0f --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/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/disableCacheCleanup/tests-kotlin/cacheRetention.sample.conf b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/tests-kotlin/cacheRetention.sample.conf new file mode 100644 index 000000000000..b2dfb8ff3006 --- /dev/null +++ b/subprojects/docs/src/snippets/initScripts/disableCacheCleanup/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/java/basic/groovy/build.gradle b/subprojects/docs/src/snippets/java/basic/groovy/build.gradle index fd3e0e56b922..a6c3cba60b3b 100644 --- a/subprojects/docs/src/snippets/java/basic/groovy/build.gradle +++ b/subprojects/docs/src/snippets/java/basic/groovy/build.gradle @@ -6,7 +6,7 @@ plugins { // tag::java-extension[] java { toolchain { - languageVersion = JavaLanguageVersion.of(11) + languageVersion = JavaLanguageVersion.of(17) } } // end::java-extension[] 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 c2a47f278d06..e972133ae013 100644 --- a/subprojects/docs/src/snippets/java/basic/kotlin/build.gradle.kts +++ b/subprojects/docs/src/snippets/java/basic/kotlin/build.gradle.kts @@ -6,7 +6,7 @@ plugins { // tag::java-extension[] java { toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) + languageVersion.set(JavaLanguageVersion.of(17)) } } // end::java-extension[] diff --git a/subprojects/docs/src/snippets/java/preview/groovy/build.gradle b/subprojects/docs/src/snippets/java/preview/groovy/build.gradle index b50fe2c554af..dce31dd7383e 100644 --- a/subprojects/docs/src/snippets/java/preview/groovy/build.gradle +++ b/subprojects/docs/src/snippets/java/preview/groovy/build.gradle @@ -3,13 +3,15 @@ plugins { } // tag::enabling-feature-preview[] -tasks.withType(JavaCompile) { +tasks.withType(JavaCompile).configureEach { options.compilerArgs += "--enable-preview" } -tasks.withType(Test) { + +tasks.withType(Test).configureEach { jvmArgs += "--enable-preview" } -tasks.withType(JavaExec) { + +tasks.withType(JavaExec).configureEach { jvmArgs += "--enable-preview" } // end::enabling-feature-preview[] diff --git a/subprojects/docs/src/snippets/java/preview/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/java/preview/kotlin/build.gradle.kts index fec00d473613..80c61f077758 100644 --- a/subprojects/docs/src/snippets/java/preview/kotlin/build.gradle.kts +++ b/subprojects/docs/src/snippets/java/preview/kotlin/build.gradle.kts @@ -3,13 +3,15 @@ plugins { } // tag::enabling-feature-preview[] -tasks.withType { +tasks.withType().configureEach { options.compilerArgs.add("--enable-preview") } -tasks.withType { + +tasks.withType().configureEach { jvmArgs("--enable-preview") } -tasks.withType { + +tasks.withType().configureEach { jvmArgs("--enable-preview") } // end::enabling-feature-preview[] diff --git a/subprojects/docs/src/snippets/java/toolchain-basic/groovy/build.gradle b/subprojects/docs/src/snippets/java/toolchain-basic/groovy/build.gradle new file mode 100644 index 000000000000..4a8d88210811 --- /dev/null +++ b/subprojects/docs/src/snippets/java/toolchain-basic/groovy/build.gradle @@ -0,0 +1,11 @@ +plugins { + id 'java' +} + +// tag::toolchain[] +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} +// end::toolchain[] diff --git a/subprojects/docs/src/snippets/java/toolchain-basic/groovy/settings.gradle b/subprojects/docs/src/snippets/java/toolchain-basic/groovy/settings.gradle new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/subprojects/docs/src/snippets/java/toolchain-basic/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/java/toolchain-basic/kotlin/build.gradle.kts new file mode 100644 index 000000000000..1584052ac8b1 --- /dev/null +++ b/subprojects/docs/src/snippets/java/toolchain-basic/kotlin/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + java +} + +// tag::toolchain[] +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} +// end::toolchain[] diff --git a/subprojects/docs/src/snippets/java/toolchain-basic/kotlin/settings.gradle.kts b/subprojects/docs/src/snippets/java/toolchain-basic/kotlin/settings.gradle.kts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/subprojects/docs/src/snippets/java/toolchain-basic/tests/toolchainBasic.sample.conf b/subprojects/docs/src/snippets/java/toolchain-basic/tests/toolchainBasic.sample.conf new file mode 100644 index 000000000000..1e81660926b4 --- /dev/null +++ b/subprojects/docs/src/snippets/java/toolchain-basic/tests/toolchainBasic.sample.conf @@ -0,0 +1,2 @@ +executable: gradle +args: build diff --git a/subprojects/docs/src/snippets/java/toolchain-task/groovy/build.gradle b/subprojects/docs/src/snippets/java/toolchain-task/groovy/build.gradle index 87c1dc89d7ca..1f1e91e8928a 100644 --- a/subprojects/docs/src/snippets/java/toolchain-task/groovy/build.gradle +++ b/subprojects/docs/src/snippets/java/toolchain-task/groovy/build.gradle @@ -1,21 +1,15 @@ import javax.inject.Inject; +// tag::custom-toolchain-task-with-java[] abstract class CustomTaskUsingToolchains extends DefaultTask { @Nested - abstract Property getLauncher() + abstract Property getLauncher() // <1> - @Inject CustomTaskUsingToolchains() { - // Access the default toolchain - def toolchain = project.getExtensions().getByType(JavaPluginExtension.class).toolchain - - // acquire a provider that returns the launcher for the toolchain - JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class) - Provider defaultLauncher = service.launcherFor(toolchain); - - // use it as our default for the property - launcher.convention(defaultLauncher); + def toolchain = project.extensions.getByType(JavaPluginExtension.class).toolchain // <2> + Provider defaultLauncher = getJavaToolchainService().launcherFor(toolchain) // <3> + launcher.convention(defaultLauncher) // <4> } @TaskAction @@ -23,22 +17,28 @@ abstract class CustomTaskUsingToolchains extends DefaultTask { println launcher.get().executablePath println launcher.get().metadata.installationPath } + + @Inject + protected abstract JavaToolchainService getJavaToolchainService() } +// end::custom-toolchain-task-with-java[] +// tag::custom-toolchain-task-with-java-usage[] plugins { id 'java' } java { - toolchain { + toolchain { // <1> languageVersion = JavaLanguageVersion.of(8) } } -tasks.register('showDefaultToolchain', CustomTaskUsingToolchains) +tasks.register('showDefaultToolchain', CustomTaskUsingToolchains) // <2> tasks.register('showCustomToolchain', CustomTaskUsingToolchains) { - launcher = javaToolchains.launcherFor { + launcher = javaToolchains.launcherFor { // <3> languageVersion = JavaLanguageVersion.of(17) } } +// end::custom-toolchain-task-with-java-usage[] diff --git a/subprojects/docs/src/snippets/java/toolchain-task/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/java/toolchain-task/kotlin/build.gradle.kts index 974b9151ba03..d35727a71f10 100644 --- a/subprojects/docs/src/snippets/java/toolchain-task/kotlin/build.gradle.kts +++ b/subprojects/docs/src/snippets/java/toolchain-task/kotlin/build.gradle.kts @@ -1,21 +1,15 @@ import javax.inject.Inject; -abstract class CustomTaskUsingToolchains : DefaultTask { +// tag::custom-toolchain-task-with-java[] +abstract class CustomTaskUsingToolchains : DefaultTask() { @get:Nested - abstract val launcher: Property + abstract val launcher: Property // <1> - @Inject - constructor() { - // Access the default toolchain - val toolchain = project.extensions.getByType().toolchain - - // acquire a provider that returns the launcher for the toolchain - val service = project.extensions.getByType() - val defaultLauncher = service.launcherFor(toolchain) - - // use it as our default for the property - launcher.convention(defaultLauncher); + init { + val toolchain = project.extensions.getByType().toolchain // <2> + val defaultLauncher = javaToolchainService.launcherFor(toolchain) // <3> + launcher.convention(defaultLauncher) // <4> } @TaskAction @@ -23,22 +17,28 @@ abstract class CustomTaskUsingToolchains : DefaultTask { println(launcher.get().executablePath) println(launcher.get().metadata.installationPath) } + + @get:Inject + protected abstract val javaToolchainService: JavaToolchainService } +// end::custom-toolchain-task-with-java[] +// tag::custom-toolchain-task-with-java-usage[] plugins { java } java { - toolchain { + toolchain { // <1> languageVersion.set(JavaLanguageVersion.of(8)) } } -tasks.register("showDefaultToolchain") +tasks.register("showDefaultToolchain") // <2> tasks.register("showCustomToolchain") { - launcher.set(javaToolchains.launcherFor { + launcher.set(javaToolchains.launcherFor { // <3> languageVersion.set(JavaLanguageVersion.of(17)) }) } +// end::custom-toolchain-task-with-java-usage[] diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/build.gradle b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/build.gradle new file mode 100644 index 000000000000..935261034d44 --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/build.gradle @@ -0,0 +1,9 @@ +plugins { + id('org.gradle.sample.taskevents') +} + +tasks.register("hello") { + doLast { + println("Hello!") + } +} diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/build.gradle b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/build.gradle new file mode 100644 index 000000000000..b391b947a234 --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/build.gradle @@ -0,0 +1,16 @@ +plugins { + id('java-gradle-plugin') +} + +repositories { + mavenCentral() +} + +gradlePlugin { + plugins { + taskEvents { + id = 'org.gradle.sample.taskevents' + implementationClass = 'TaskEventsPlugin' + } + } +} diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsPlugin.java b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsPlugin.java new file mode 100644 index 000000000000..b99797e300d6 --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsPlugin.java @@ -0,0 +1,20 @@ +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.provider.Provider; +import org.gradle.build.event.BuildEventsListenerRegistry; + +import javax.inject.Inject; + +public abstract class TaskEventsPlugin implements Plugin { + @Inject + public abstract BuildEventsListenerRegistry getEventsListenerRegistry(); // <1> + + @Override + public void apply(Project project) { + Provider serviceProvider = + project.getGradle().getSharedServices().registerIfAbsent( + "taskEvents", TaskEventsService.class, spec -> {}); // <2> + + getEventsListenerRegistry().onTaskCompletion(serviceProvider); // <3> + } +} diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsService.java b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsService.java new file mode 100644 index 000000000000..368b2766da06 --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsService.java @@ -0,0 +1,22 @@ +// tag::build-service[] +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; +import org.gradle.tooling.events.FinishEvent; +import org.gradle.tooling.events.OperationCompletionListener; +import org.gradle.tooling.events.task.TaskFinishEvent; + +public abstract class TaskEventsService implements BuildService, + OperationCompletionListener { // <1> + + @Override + public void onFinish(FinishEvent finishEvent) { + if (finishEvent instanceof TaskFinishEvent) { // <2> + // Handle task finish event... +// end::build-service[] + System.out.println( + "Task finished = " + ((TaskFinishEvent) finishEvent).getDescriptor().getTaskPath()); +// tag::build-service[] + } + } +} +// end::build-service[] diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/settings.gradle b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/settings.gradle new file mode 100644 index 000000000000..a5a79ef020f9 --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'build-service-listener' diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.out b/subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.out new file mode 100644 index 000000000000..2daf46ab5650 --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.out @@ -0,0 +1,2 @@ +Hello! +Task finished = :hello diff --git a/subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.sample.conf b/subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.sample.conf new file mode 100644 index 000000000000..dae50b45b7fc --- /dev/null +++ b/subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.sample.conf @@ -0,0 +1,3 @@ +executable: gradle +args: "--quiet hello" +expected-output-file: registerListener.out diff --git a/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPluginConvention.java b/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPluginConvention.java index 323d620f6630..b51868c30d8c 100644 --- a/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPluginConvention.java +++ b/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPluginConvention.java @@ -24,7 +24,7 @@ /** * Ear Plugin Convention. * - * @deprecated Instead of using conventions, configure the tasks directly. This class is scheduled for removal in Gradle 8.0. + * @deprecated Instead of using conventions, configure the tasks directly. This class is scheduled for removal in Gradle 9.0. */ @Deprecated public abstract class EarPluginConvention { diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginCheckInIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginCheckInIntegrationTest.groovy index 7537a9ba3244..d9da2b4084f9 100644 --- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginCheckInIntegrationTest.groovy +++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginCheckInIntegrationTest.groovy @@ -17,8 +17,14 @@ package org.gradle.internal.enterprise import org.gradle.integtests.fixtures.AbstractIntegrationSpec +import org.gradle.integtests.fixtures.executer.GradleContextualExecuter import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager -import org.gradle.internal.enterprise.impl.DefautGradleEnterprisePluginCheckInService +import spock.lang.IgnoreIf + +import static org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService.MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING +import static org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE +import static org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE +import static org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE_MESSAGE class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSpec { @@ -33,7 +39,6 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp """ } - void applyPlugin() { settingsFile << plugin.plugins() } @@ -63,10 +68,10 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp applyPlugin() when: - succeeds "t", "-D${DefautGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE}=true" + succeeds "t", "-D${UNSUPPORTED_TOGGLE}=true" then: - plugin.assertUnsupportedMessage(output, DefautGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE_MESSAGE) + plugin.assertUnsupportedMessage(output, UNSUPPORTED_TOGGLE_MESSAGE) } def "checkin happens once for build with buildSrc"() { @@ -86,4 +91,33 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp then: plugin.serviceCreatedOnce(output) } + + @IgnoreIf({ GradleContextualExecuter.configCache }) + def "shows warning message when unsupported Gradle Enterprise plugin version is used with configuration caching enabled"() { + given: + plugin.runtimeVersion = plugin.artifactVersion = pluginVersion + applyPlugin() + settingsFile << """ + println "present: " + services.get($GradleEnterprisePluginManager.name).present + """ + + when: + succeeds("t", "--configuration-cache") + + then: + output.contains("present: ${applied}") + + and: + output.contains("gradleEnterprisePlugin.checkIn.unsupported.reasonMessage = $UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE") != applied + + where: + pluginVersion | applied + '3.11.4' | false + getMinimumPluginVersionForConfigurationCaching() | true + } + + private static String getMinimumPluginVersionForConfigurationCaching() { + "${MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.getMajor()}.${MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.getMinor()}" + } + } diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanAutoApplyIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanAutoApplyIntegrationTest.groovy index 4df9837b7568..e57c72cb59b3 100644 --- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanAutoApplyIntegrationTest.groovy +++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanAutoApplyIntegrationTest.groovy @@ -18,7 +18,7 @@ package org.gradle.internal.enterprise.core import org.gradle.integtests.fixtures.AbstractIntegrationSpec import org.gradle.internal.enterprise.GradleEnterprisePluginCheckInFixture -import org.gradle.internal.enterprise.impl.DefautGradleEnterprisePluginCheckInService +import org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService import org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService import org.gradle.plugin.management.internal.autoapply.AutoAppliedGradleEnterprisePlugin import org.gradle.util.internal.VersionNumber @@ -57,7 +57,7 @@ class BuildScanAutoApplyIntegrationTest extends AbstractIntegrationSpec { def "only applies once when -b used"() { when: file("other-build.gradle") << "task dummy {}" - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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") runBuildWithScanRequest("-b", "other-build.gradle") then: @@ -257,10 +257,10 @@ class BuildScanAutoApplyIntegrationTest extends AbstractIntegrationSpec { applyPlugin() when: - succeeds "dummy", "--scan", "-D${DefautGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE}=true" + succeeds "dummy", "--scan", "-D${DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE}=true" then: - fixture.assertUnsupportedMessage(output, DefautGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE_MESSAGE) + fixture.assertUnsupportedMessage(output, DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE_MESSAGE) fixture.didNotIssuedNoPluginWarning(output) } diff --git a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java new file mode 100644 index 000000000000..484bf43c2ac1 --- /dev/null +++ b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java @@ -0,0 +1,95 @@ +/* + * 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.enterprise.impl; + +import org.gradle.internal.buildtree.BuildModelParameters; +import org.gradle.internal.enterprise.GradleEnterprisePluginCheckInResult; +import org.gradle.internal.enterprise.GradleEnterprisePluginCheckInService; +import org.gradle.internal.enterprise.GradleEnterprisePluginMetadata; +import org.gradle.internal.enterprise.GradleEnterprisePluginServiceFactory; +import org.gradle.internal.enterprise.GradleEnterprisePluginServiceRef; +import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager; +import org.gradle.util.internal.VersionNumber; + +import java.util.function.Supplier; + +public class DefaultGradleEnterprisePluginCheckInService implements GradleEnterprisePluginCheckInService { + + private final GradleEnterprisePluginManager manager; + private final DefaultGradleEnterprisePluginAdapter adapter; + private final boolean isConfigurationCacheEnabled; + + public DefaultGradleEnterprisePluginCheckInService( + BuildModelParameters buildModelParameters, + GradleEnterprisePluginManager manager, + DefaultGradleEnterprisePluginAdapter adapter + ) { + this.manager = manager; + this.adapter = adapter; + this.isConfigurationCacheEnabled = buildModelParameters.isConfigurationCache(); + } + + // Used just for testing + public static final String UNSUPPORTED_TOGGLE = "org.gradle.internal.unsupported-enterprise-plugin"; + public static final String UNSUPPORTED_TOGGLE_MESSAGE = "Enterprise plugin unsupported due to secret toggle"; + + // For Gradle versions 8+, configuration caching builds are not compatible with Gradle Enterprise plugin < 3.12 + public static final VersionNumber MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING = VersionNumber.version(3, 12); + public static final String UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE = String.format("The Gradle Enterprise plugin has been disabled as it is " + + "incompatible with this version of Gradle and the configuration caching feature - please upgrade to version %s.%s or later of the Gradle Enterprise plugin to restore functionality.", + MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.getMajor(), + MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.getMinor()); + + @Override + public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadata pluginMetadata, GradleEnterprisePluginServiceFactory serviceFactory) { + if (Boolean.getBoolean(UNSUPPORTED_TOGGLE)) { + manager.unsupported(); + return checkInResult(UNSUPPORTED_TOGGLE_MESSAGE, () -> { + throw new IllegalStateException(); + }); + } + if (isUnsupportedWithConfigurationCaching(pluginMetadata)) { + manager.unsupported(); + return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE, () -> { + throw new IllegalStateException(); + }); + } + GradleEnterprisePluginServiceRef ref = adapter.register(serviceFactory); + manager.registerAdapter(adapter); + return checkInResult(null, () -> ref); + } + + private static GradleEnterprisePluginCheckInResult checkInResult(String unsupportedMessage, Supplier pluginServiceRefSupplier) { + return new GradleEnterprisePluginCheckInResult() { + @Override + public String getUnsupportedMessage() { + return unsupportedMessage; + } + + @Override + public GradleEnterprisePluginServiceRef getPluginServiceRef() { + return pluginServiceRefSupplier.get(); + } + }; + } + + private boolean isUnsupportedWithConfigurationCaching(GradleEnterprisePluginMetadata pluginMetadata) { + VersionNumber version = VersionNumber.parse(pluginMetadata.getVersion()).getBaseVersion(); + return isConfigurationCacheEnabled && MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.compareTo(version) > 0; + } + +} diff --git a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefautGradleEnterprisePluginCheckInService.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefautGradleEnterprisePluginCheckInService.java deleted file mode 100644 index 67c90261750d..000000000000 --- a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefautGradleEnterprisePluginCheckInService.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.internal.enterprise.impl; - -import org.gradle.internal.enterprise.GradleEnterprisePluginCheckInResult; -import org.gradle.internal.enterprise.GradleEnterprisePluginCheckInService; -import org.gradle.internal.enterprise.GradleEnterprisePluginMetadata; -import org.gradle.internal.enterprise.GradleEnterprisePluginServiceFactory; -import org.gradle.internal.enterprise.GradleEnterprisePluginServiceRef; -import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager; - -import javax.annotation.Nullable; - -public class DefautGradleEnterprisePluginCheckInService implements GradleEnterprisePluginCheckInService { - - private final GradleEnterprisePluginManager manager; - private final DefaultGradleEnterprisePluginAdapter adapter; - - public DefautGradleEnterprisePluginCheckInService( - GradleEnterprisePluginManager manager, - DefaultGradleEnterprisePluginAdapter adapter - ) { - this.manager = manager; - this.adapter = adapter; - } - - // Used just for testing - public static final String UNSUPPORTED_TOGGLE = "org.gradle.internal.unsupported-enterprise-plugin"; - public static final String UNSUPPORTED_TOGGLE_MESSAGE = "Enterprise plugin unsupported due to secret toggle"; - - @Override - public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadata pluginMetadata, GradleEnterprisePluginServiceFactory serviceFactory) { - if (Boolean.getBoolean(UNSUPPORTED_TOGGLE)) { - manager.unsupported(); - return new GradleEnterprisePluginCheckInResult() { - @Override - public String getUnsupportedMessage() { - return UNSUPPORTED_TOGGLE_MESSAGE; - } - - @Override - public GradleEnterprisePluginServiceRef getPluginServiceRef() { - throw new IllegalStateException(); - } - }; - } else { - GradleEnterprisePluginServiceRef ref = adapter.register(serviceFactory); - manager.registerAdapter(adapter); - return new GradleEnterprisePluginCheckInResult() { - @Nullable - @Override - public String getUnsupportedMessage() { - return null; - } - - @Override - public GradleEnterprisePluginServiceRef getPluginServiceRef() { - return ref; - } - }; - } - } - -} diff --git a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/GradleEnterprisePluginServices.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/GradleEnterprisePluginServices.java index 11a2b4a3149d..f762151b0fc3 100644 --- a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/GradleEnterprisePluginServices.java +++ b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/GradleEnterprisePluginServices.java @@ -61,7 +61,7 @@ public void registerGradleServices(ServiceRegistration registration) { registration.add(DefaultGradleEnterprisePluginBackgroundJobExecutors.class); registration.add(DefaultGradleEnterprisePluginBuildState.class); registration.add(DefaultGradleEnterprisePluginConfig.class); - registration.add(DefautGradleEnterprisePluginCheckInService.class); + registration.add(DefaultGradleEnterprisePluginCheckInService.class); registration.add(DefaultGradleEnterprisePluginRequiredServices.class); registration.add(DefaultGradleEnterprisePluginServiceRef.class); diff --git a/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/AbstractMultiBuildIdeIntegrationTest.groovy b/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/AbstractMultiBuildIdeIntegrationTest.groovy index 9d5b61624fee..392449e2c445 100644 --- a/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/AbstractMultiBuildIdeIntegrationTest.groovy +++ b/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/AbstractMultiBuildIdeIntegrationTest.groovy @@ -31,15 +31,15 @@ abstract class AbstractMultiBuildIdeIntegrationTest extends AbstractIntegrationS abstract IdeWorkspaceFixture workspace(TestFile workspaceDir, String ideWorkspaceName) abstract IdeProjectFixture project(TestFile projectDir, String ideProjectName) + @ToBeFixedForConfigurationCache(because = "ide plugins") @Issue("https://github.com/gradle/gradle/issues/5110") def "buildSrc project can apply IDE plugin"() { file("buildSrc/build.gradle") << """ apply plugin: '${pluginId}' - tasks.build.dependsOn tasks.${workspaceTask} """ expect: - succeeds() + succeeds(":buildSrc:${workspaceTask}") def workspace = workspace(file("buildSrc"), "buildSrc") if (libraryPluginId == "java-library") { def project = project(file("buildSrc"), "buildSrc") diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java index 65a9d6985e04..9c1428bcbb10 100644 --- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java +++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLoadingIntegrationTest.java @@ -270,7 +270,7 @@ public void specifyingCustomSettingsFileIsDeprecated() { TestFile subSettingsFile = subDirectory.file("renamed_settings.gradle").write(""); subDirectory.file("build.gradle").write(""); - executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 8.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: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout"); inDirectory(subDirectory).usingSettingsFile(subSettingsFile).withTasks("help").run(); } @@ -279,7 +279,7 @@ public void specifyingCustomBuildFileIsDeprecated() { testFile("settings.gradle").write("include 'another'"); TestFile renamedBuildGradle = file("renamed_build.gradle").createFile(); - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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.usingBuildScript(renamedBuildGradle).withTasks("help").run(); } 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 407585a8b3fb..6be1f8f0863e 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 @@ -56,6 +56,7 @@ import static org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout.DEFA import static org.gradle.test.fixtures.dsl.GradleDsl.GROOVY import static org.gradle.util.Matchers.matchesRegexp import static org.gradle.util.Matchers.normalizedLineSeparators + /** * Spockified version of AbstractIntegrationTest. * diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy index 40febcde0106..3df298493fe9 100755 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/CrossVersionIntegrationSpec.groovy @@ -29,6 +29,9 @@ import spock.lang.Specification import static spock.lang.Retry.Mode.SETUP_FEATURE_CLEANUP +/** + * For running these tests against specific versions, see {@link org.gradle.integtests.fixtures.compatibility.AbstractContextualMultiVersionTestInterceptor} + */ @CrossVersionTest @Retry(condition = { RetryConditions.onIssueWithReleasedGradleVersion(instance, failure) }, mode = SETUP_FEATURE_CLEANUP, count = 2) abstract class CrossVersionIntegrationSpec extends Specification { 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 b17ffbf38871..5bd551db66e8 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 @@ -308,6 +308,11 @@ class DefaultGradleDistribution implements GradleDistribution { return isSameOrNewer("8.0") } + @Override + boolean isRunsBuildSrcTests() { + return isSameOrOlder("7.6") + } + @Override T selectOutputWithFailureLogging(T stdout, T stderr) { if (isSameOrNewer("4.0") && isSameOrOlder("4.6") || isSameOrNewer("5.1-rc-1")) { 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 5c5f4a41e5fb..42a3b0acc347 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 @@ -170,4 +170,9 @@ public interface GradleDistribution { * Returns true if this version loads the work graph from the configuration cache in the same build that the entry is stored. */ boolean isLoadsFromConfigurationCacheAfterStore(); + + /** + * Returns true if this version runs tests when building `buildSrc` + */ + boolean isRunsBuildSrcTests(); } diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainBuildOperationsFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainBuildOperationsFixture.groovy index 5b61d4a0c83b..f020db3af9fd 100644 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainBuildOperationsFixture.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainBuildOperationsFixture.groovy @@ -56,9 +56,28 @@ trait JavaToolchainBuildOperationsFixture { } } + static void assertToolchainUsages(List events, JvmInstallationMetadata jdkMetadata, String... tools) { + assert events.size() > 0 + def expectedTools = tools.toList().sort() + def usedTools = events.collect { it.details.toolName }.unique().sort() + assert expectedTools == usedTools + events.each { usageEvent -> + def usedToolchain = usageEvent.details.toolchain + assert usedToolchain == [ + javaVersion: jdkMetadata.javaVersion, + javaVendor: jdkMetadata.vendor.displayName, + runtimeName: jdkMetadata.runtimeName, + runtimeVersion: jdkMetadata.runtimeVersion, + jvmName: jdkMetadata.jvmName, + jvmVersion: jdkMetadata.jvmVersion, + jvmVendor: jdkMetadata.jvmVendor, + architecture: jdkMetadata.architecture, + ] + } + } + static void assertToolchainUsage(String toolName, JvmInstallationMetadata jdkMetadata, BuildOperationRecord.Progress usageEvent) { assert usageEvent.details.toolName == toolName - def usedToolchain = usageEvent.details.toolchain assert usedToolchain == [ javaVersion: jdkMetadata.javaVersion, diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainFixture.groovy index f3a696ad132c..a04e2546b5e4 100644 --- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainFixture.groovy +++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/jvm/JavaToolchainFixture.groovy @@ -17,6 +17,7 @@ package org.gradle.integtests.fixtures.jvm import groovy.transform.SelfType +import org.gradle.api.JavaVersion import org.gradle.integtests.fixtures.AbstractIntegrationSpec import org.gradle.internal.jvm.Jvm import org.gradle.internal.jvm.inspection.JvmInstallationMetadata @@ -91,4 +92,12 @@ trait JavaToolchainFixture { .withArgument("-Porg.gradle.java.installations.paths=" + installationPaths) this as AbstractIntegrationSpec } + + /** + * Returns the Java version from the compiled class bytecode. + */ + JavaVersion classJavaVersion(File classFile) { + assert classFile.exists() + return JavaVersion.forClass(classFile.bytes) + } } 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 b834d37286d8..98d64a68171c 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 @@ -24,7 +24,8 @@ class KotlinGradlePluginVersions { // https://search.maven.org/search?q=g:org.jetbrains.kotlin%20AND%20a:kotlin-project&core=gav private static final List LATEST_VERSIONS = [ '1.6.10', '1.6.21', - '1.7.0', '1.7.10', "1.7.22" + '1.7.0', '1.7.10', "1.7.22", + "1.8.0-RC", ] List getLatests() { 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 35d01cbc1cf8..026d87583851 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 @@ -397,33 +397,27 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe def "emits toolchain usages when configuring toolchains for #kotlinPlugin Kotlin plugin '#kotlinPluginVersion'"() { JvmInstallationMetadata jdkMetadata = AvailableJavaHomes.getJvmInstallationMetadata(AvailableJavaHomes.differentVersion) + given: // override setup buildFile.text = """ - buildscript { - ${mavenCentralRepository()} - dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinPluginVersion" } + plugins { + id("org.jetbrains.kotlin.jvm") version "$kotlinPluginVersion" } - - apply plugin: "kotlin" - ${mavenCentralRepository()} dependencies { testImplementation 'junit:junit:4.13' } - kotlin { jvmToolchain { languageVersion = JavaLanguageVersion.of(${jdkMetadata.languageVersion.majorVersion}) } } """ - file("src/main/kotlin/Foo.kt") << """ class Foo { fun random() = 4 } """ - file("src/test/kotlin/FooTest.kt") << """ class FooTest { @org.junit.Test @@ -431,9 +425,13 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe } """ - when: + and: + def kotlinVersionNumber = VersionNumber.parse(kotlinPluginVersion) + def isKotlin1dot6 = kotlinVersionNumber.baseVersion < VersionNumber.parse("1.7.0") + def isKotlin1dot8 = kotlinVersionNumber.baseVersion >= VersionNumber.parse("1.8.0") - if (VersionNumber.parse(kotlinPluginVersion) <= VersionNumber.parse("1.6.21")) { + when: + if (isKotlin1dot6) { executer.expectDocumentedDeprecationWarning( "The AbstractCompile.destinationDir property has been deprecated. " + "This is scheduled to be removed in Gradle 9.0. " + @@ -443,10 +441,17 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe withInstallations(jdkMetadata).run(":compileKotlin", ":test") def eventsOnCompile = toolchainEvents(":compileKotlin") def eventsOnTest = toolchainEvents(":test") + then: executedAndNotSkipped(":compileKotlin", ":test") - // The tool is a launcher, because kotlin runs own compilation in a Java VM - assertToolchainUsages(eventsOnCompile, jdkMetadata, "JavaLauncher") + println(eventsOnCompile) + if (isKotlin1dot8) { + // Kotlin 1.8 uses both launcher and compiler + assertToolchainUsages(eventsOnCompile, jdkMetadata, "JavaLauncher", "JavaCompiler") + } else { + // The tool is a launcher with Kotlin < 1.8, because it runs own compilation in a Java VM + assertToolchainUsages(eventsOnCompile, jdkMetadata, "JavaLauncher") + } // Even though we only configure the toolchain within the `kotlin` block, // it actually affects the java launcher selected by the test task. assertToolchainUsages(eventsOnTest, jdkMetadata, "JavaLauncher") @@ -455,8 +460,9 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe withInstallations(jdkMetadata).run(":compileKotlin", ":test") eventsOnCompile = toolchainEvents(":compileKotlin") eventsOnTest = toolchainEvents(":test") + then: - if (!isAlwaysUpToDate && Jvm.current().javaVersion.java8 && GradleContextualExecuter.configCache) { + if (isKotlin1dot6 && Jvm.current().javaVersion.java8 && GradleContextualExecuter.configCache) { // For Kotlin 1.6 the compilation is not up-to-date with configuration caching when running on Java 8 executedAndNotSkipped(":compileKotlin") } else { @@ -466,10 +472,10 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe assertToolchainUsages(eventsOnTest, jdkMetadata, "JavaLauncher") where: - kotlinPlugin | isAlwaysUpToDate - "1.6" | false - "1.7" | true - "latest" | true + kotlinPlugin | _ + "1.6" | _ + "1.7" | _ + "latest" | _ kotlinPluginVersion = kotlinPlugin == "latest" ? kgpLatestVersions.last() : latestStableKotlinPluginVersion(kotlinPlugin) } diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileCompatibilityIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileCompatibilityIntegrationTest.groovy new file mode 100644 index 000000000000..b6ff230bb92b --- /dev/null +++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileCompatibilityIntegrationTest.groovy @@ -0,0 +1,561 @@ +/* + * 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.tasks.compile + +import org.gradle.api.JavaVersion +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 org.gradle.internal.os.OperatingSystem +import org.gradle.util.internal.TextUtil +import spock.lang.Issue + +import static org.junit.Assume.assumeNotNull + +class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec implements JavaToolchainFixture { + + def setup() { + file("src/main/java/Foo.java") << "public class Foo {}" + } + + def "source compatibility convention is set and used for target compatibility convention"() { + def jdk11 = AvailableJavaHomes.getJdk11() + + buildFile << """ + apply plugin: "java-base" + + java { + ${extensionSource != null ? "sourceCompatibility = JavaVersion.toVersion('$extensionSource')" : ""} + } + + sourceSets { + custom {} + } + + compileCustomJava { + ${taskSource != null ? "sourceCompatibility = '$taskSource'" : ""} + ${taskRelease != null ? "options.release = $taskRelease" : ""} + ${taskToolchain != null ? "javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of($taskToolchain) }" : ""} + } + + compileCustomJava.doLast { + logger.lifecycle("task.sourceCompatibility = '\$sourceCompatibility'") + logger.lifecycle("task.targetCompatibility = '\$targetCompatibility'") + } + """ + + file("src/custom/java/Test.java") << """public class Test { }""" + + when: + withInstallations(jdk11).succeeds("compileCustomJava") + + then: + executedAndNotSkipped(":compileCustomJava") + outputContains("task.sourceCompatibility = '$sourceOut'") + outputContains("task.targetCompatibility = '$targetOut'") + classJavaVersion(classFile("java", "custom", "Test.class")) == JavaVersion.toVersion(targetOut) + + where: + taskSource | taskRelease | extensionSource | taskToolchain | sourceOut | targetOut + "9" | "8" | "11" | "11" | "9" | "1.8" + null | "9" | "11" | "11" | "9" | "9" + null | null | "9" | "11" | "9" | "9" + null | null | null | "11" | "11" | "11" + null | null | null | null | currentJavaVersion() | currentJavaVersion() + } + + def "target compatibility convention is set"() { + def jdk11 = AvailableJavaHomes.getJdk11() + + buildFile << """ + apply plugin: "java-base" + + java { + ${extensionTarget != null ? "targetCompatibility = JavaVersion.toVersion(${extensionTarget})" : ""} + } + + sourceSets { + custom {} + } + + compileCustomJava { + ${taskTarget != null ? "targetCompatibility = '${taskTarget}'" : ""} + ${taskRelease ? "options.release = ${taskRelease}" : ""} + ${taskSource ? "sourceCompatibility = '${taskSource}'" : ""} + ${taskToolchain != null ? "javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(${taskToolchain}) }" : ""} + } + + compileCustomJava.doLast { + logger.lifecycle("task.sourceCompatibility = \$sourceCompatibility") + logger.lifecycle("task.targetCompatibility = \$targetCompatibility") + } + """ + + file("src/custom/java/Test.java") << """public class Test { }""" + + when: + withInstallations(jdk11).succeeds("compileCustomJava") + + then: + executedAndNotSkipped(":compileCustomJava") + outputContains("task.sourceCompatibility = $sourceOut") + outputContains("task.targetCompatibility = $targetOut") + classJavaVersion(classFile("java", "custom", "Test.class")) == JavaVersion.toVersion(bytecodeOut) + + where: + taskTarget | taskRelease | extensionTarget | taskSource | taskToolchain | sourceOut | targetOut | bytecodeOut + "9" | "8" | "11" | "11" | "11" | "11" | "9" | "1.8" + "9" | null | "11" | "8" | "11" | "8" | "9" | "9" + null | "9" | "11" | "11" | "11" | "11" | "9" | "9" + null | null | "9" | "8" | "11" | "8" | "9" | "9" + null | null | null | "9" | "11" | "9" | "9" | "9" + null | null | null | null | "11" | "11" | "11" | "11" + null | null | null | null | null | currentJavaVersion() | currentJavaVersion() | currentJavaVersion() + } + + @Issue("https://github.com/gradle/gradle/issues/22397") + def "uses source and target compatibility from toolchain defined by forkOptions #forkOption"() { + def currentJdk = Jvm.current() + def earlierJdk = AvailableJavaHomes.getDifferentVersion { it.languageVersion < currentJdk.javaVersion } + assumeNotNull(earlierJdk) + + def path = TextUtil.normaliseFileSeparators(earlierJdk.javaHome.absolutePath.toString() + appendPath) + + buildFile << """ + apply plugin: "java" + + compileJava { + options.fork = true + ${configure(path)} + } + + compileJava.doLast { + println "sourceCompatibility: '\${sourceCompatibility}'" + println "targetCompatibility: '\${targetCompatibility}'" + } + """ + + when: + withInstallations(earlierJdk).run(":compileJava", "--info") + + then: + executedAndNotSkipped(":compileJava") + outputContains("Compiling with toolchain '${earlierJdk.javaHome.absolutePath}'") + outputContains("sourceCompatibility: '${earlierJdk.javaVersion}'") + outputContains("targetCompatibility: '${earlierJdk.javaVersion}'") + classJavaVersion(javaClassFile("Foo.class")) == earlierJdk.javaVersion + + where: + forkOption | configure | appendPath + "java home" | { "options.forkOptions.javaHome = file('$it')" } | '' + "executable" | { "options.forkOptions.executable = '$it'" } | OperatingSystem.current().getExecutableName('/bin/javac') + } + + def "source and target compatibility override toolchain (source #source, target #target)"() { + def jdk11 = AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) + + buildFile << """ + apply plugin: 'java' + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } + } + + compileJava { + ${source != 'none' ? "sourceCompatibility = JavaVersion.toVersion($source)" : ''} + ${target != 'none' ? "targetCompatibility = JavaVersion.toVersion($target)" : ''} + } + + compileJava { + def projectSourceCompat = project.java.sourceCompatibility + def projectTargetCompat = project.java.targetCompatibility + doLast { + logger.lifecycle("project.sourceCompatibility = '\${projectSourceCompat}'") + logger.lifecycle("project.targetCompatibility = '\${projectTargetCompat}'") + logger.lifecycle("task.sourceCompatibility = '\$sourceCompatibility'") + logger.lifecycle("task.targetCompatibility = '\$targetCompatibility'") + } + } + """ + + when: + withInstallations(jdk11).run(":compileJava") + + then: + outputContains("project.sourceCompatibility = '11'") + outputContains("project.targetCompatibility = '11'") + outputContains("task.sourceCompatibility = '$sourceOut'") + outputContains("task.targetCompatibility = '$targetOut'") + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(targetOut) + + where: + source | target | sourceOut | targetOut + '9' | '10' | '9' | '10' + '9' | 'none' | '9' | '9' + 'none' | 'none' | '11' | '11' + } + + def "configuring toolchain on java extension with source and target compatibility is supported"() { + def jdk = Jvm.current() + def prevJavaVersion = JavaVersion.toVersion(jdk.javaVersion.majorVersion.toInteger() - 1) + buildFile << """ + apply plugin: 'java' + + java { + sourceCompatibility = JavaVersion.toVersion('$prevJavaVersion') + targetCompatibility = JavaVersion.toVersion('$prevJavaVersion') + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava { + def projectSourceCompat = project.java.sourceCompatibility + def projectTargetCompat = project.java.targetCompatibility + doLast { + logger.lifecycle("project.sourceCompatibility = '\${projectSourceCompat}'") + logger.lifecycle("project.targetCompatibility = '\${projectTargetCompat}'") + logger.lifecycle("task.sourceCompatibility = '\$sourceCompatibility'") + logger.lifecycle("task.targetCompatibility = '\$targetCompatibility'") + } + } + """ + + when: + withInstallations(jdk).run(":compileJava") + + then: + outputContains("project.sourceCompatibility = '$prevJavaVersion'") + outputContains("project.targetCompatibility = '$prevJavaVersion'") + outputContains("task.sourceCompatibility = '$prevJavaVersion'") + outputContains("task.targetCompatibility = '$prevJavaVersion'") + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(prevJavaVersion) + } + + def sourceUsingLanguageFeatureFromJava17() { + """ + // Sealed classes and interfaces are only available in Java 17 + public sealed interface Parent permits Parent.Child { + public static record Child(String name) implements Parent {} + } + """ + } + + def "source compatibility lower than compiler version does not allow accessing newer Java language features"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + // Lower than Java 17 + compileJava.sourceCompatibility = "${JavaVersion.VERSION_11}" + """ + + file("src/main/java/Parent.java") << sourceUsingLanguageFeatureFromJava17() + + when: + withInstallations(jdk).fails(":compileJava") + + then: + failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") + javaClassFile("Parent.class").assertDoesNotExist() + } + + def "source compatibility matching the compiler version allows accessing Java language features"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.sourceCompatibility = "${JavaVersion.VERSION_17}" + """ + + file("src/main/java/Parent.java") << sourceUsingLanguageFeatureFromJava17() + + when: + withInstallations(jdk).succeeds(":compileJava") + + then: + executedAndNotSkipped(":compileJava") + classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 + } + + def "release flag lower than compiler version does not allow accessing newer Java language features"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + // Lower than Java 17 + compileJava.options.release = ${JavaVersion.VERSION_11.majorVersion} + """ + + file("src/main/java/Parent.java") << sourceUsingLanguageFeatureFromJava17() + + when: + withInstallations(jdk).fails(":compileJava") + + then: + failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") + javaClassFile("Parent.class").assertDoesNotExist() + } + + def "release flag matching the compiler version allows accessing corresponding Java language features"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.options.release = ${JavaVersion.VERSION_17.majorVersion} + """ + + file("src/main/java/Parent.java") << sourceUsingLanguageFeatureFromJava17() + + when: + withInstallations(jdk).succeeds(":compileJava") + + then: + executedAndNotSkipped(":compileJava") + classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 + } + + def sourceUsingJavaApiFromJava15() { + """ + public class Main { + public static void main(String[] args) { + System.out.println("Main: java " + System.getProperty("java.version")); + + // API added in Java 15 + long x = Math.absExact(-42); + System.out.println("Main: value " + x); + } + } + """ + } + + def "source compatibility lower than compiler version allows accessing newer JDK APIs"() { + def jdk11 = AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) + def jdk17 = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + apply plugin: "application" + + application.mainClass = "Main" + + java { + sourceCompatibility = "${JavaVersion.VERSION_11}" + } + + def compile11 = providers.gradleProperty("compile11").isPresent() + compileJava { + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(compile11 ? ${jdk11.javaVersion.majorVersion} : ${jdk17.javaVersion.majorVersion}) + } + } + + def run11 = providers.gradleProperty("run11").isPresent() + run { + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(run11 ? ${jdk11.javaVersion.majorVersion} : ${jdk17.javaVersion.majorVersion}) + } + } + """ + + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() + + // Compiling with Java 11 fails, because the source code uses a Java 15 API + when: + withInstallations(jdk11).fails(":compileJava", "-Pcompile11") + then: + failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") + failure.assertHasErrorOutput("symbol: method absExact(int)") + javaClassFile("Main.class").assertDoesNotExist() + + // Compiling with Java 17 works, because the source does not use any language features that are not supported by Java 11 source compatibility + when: + withInstallations(jdk17).succeeds(":compileJava") + then: + executedAndNotSkipped(":compileJava") + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_11 + + // Running with JVM 17 works, because the required Java 15 API is available at runtime + when: + withInstallations(jdk17).succeeds(":run") + then: + executedAndNotSkipped(":run") + outputContains("Main: java 17.") + outputContains("Main: value 42") + + // Running with JVM 11 fails, but only at the point where we try to use the Java 15 API + when: + withInstallations(jdk11, jdk17).fails(":run", "-Prun11") + then: + outputContains("Main: java 11.") + failure.assertHasErrorOutput("Exception in thread \"main\" java.lang.NoSuchMethodError: 'int java.lang.Math.absExact(int)'\n") + } + + def "release flag lower than compiler version does not allow accessing newer JDK APIs"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + // Lower than Java 15 + compileJava.options.release = ${JavaVersion.VERSION_11.majorVersion} + """ + + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() + + when: + withInstallations(jdk).fails(":compileJava") + + then: + failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") + failure.assertHasErrorOutput("symbol: method absExact(int)") + javaClassFile("Main.class").assertDoesNotExist() + } + + def "release flag matching the compiler version allows accessing corresponding JDK APIs"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.options.release = ${JavaVersion.VERSION_17.majorVersion} + """ + + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() + + when: + withInstallations(jdk).succeeds(":compileJava") + + then: + executedAndNotSkipped(":compileJava") + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 + } + + def "earlier toolchain does not allow accessing later JDK APIs in source"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.doFirst { + logger.lifecycle("Source is set to '\${compileJava.sourceCompatibility}'") + logger.lifecycle("Release is set to '\${compileJava.options.release.getOrNull()}'") + } + """ + + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() + + when: + withInstallations(jdk).fails(":compileJava") + + then: + // Configuring a toolchain only affects sourceCompatibility and not release + outputContains("Source is set to '${JavaVersion.VERSION_11}'") + outputContains("Release is set to 'null'") + + // But compilation still fails, because the compiler is effectively older and does not support the newer APIs + failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") + failure.assertHasErrorOutput("symbol: method absExact(int)") + javaClassFile("Main.class").assertDoesNotExist() + } + + def "toolchain allows accessing corresponding JDK APIs in source"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.doFirst { + logger.lifecycle("Source is set to '\${compileJava.sourceCompatibility}'") + logger.lifecycle("Release is set to '\${compileJava.options.release.getOrNull()}'") + } + """ + + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() + + when: + withInstallations(jdk).succeeds(":compileJava") + + then: + executedAndNotSkipped(":compileJava") + + // Configuring a toolchain only affects sourceCompatibility and not release + outputContains("Source is set to '${JavaVersion.VERSION_17}'") + outputContains("Release is set to 'null'") + + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 + } + + private static String currentJavaVersion() { + return Jvm.current().javaVersion.toString() + } +} 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 d7ed0bed6ffd..9a798dfdfc02 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 @@ -1103,7 +1103,7 @@ class JavaCompileIntegrationTest extends AbstractIntegrationSpec { 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.") + 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.") then: succeeds("compileJava") 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 9ad713a4c257..835a852af111 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 @@ -145,7 +145,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: executedAndNotSkipped(":compileJava") outputContains("Compiling with toolchain '${targetJdk.javaHome.absolutePath}'") - targetJdk.javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) + classJavaVersion(javaClassFile("Foo.class")) == targetJdk.javaVersion where: // Some cases are skipped, because forkOptions (when configured) must match the resulting toolchain, otherwise the build fails @@ -199,6 +199,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: executedAndNotSkipped(":compileJava") outputContains("Compiling with toolchain '${targetJdk.javaHome.absolutePath}'") + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(compileWithVersion) where: // Some cases are skipped, because forkOptions (when configured) must match the resulting toolchain, otherwise the build fails @@ -235,45 +236,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: executedAndNotSkipped(":compileJava") outputContains("Compiling with toolchain '${otherJdk.javaHome.absolutePath}'") - JavaVersion.toVersion(compatibilityVersion) == JavaVersion.forClass(javaClassFile("Foo.class").bytes) - - where: - forkOption | configure | appendPath - "java home" | 'options.forkOptions.javaHome = file("")' | '' - "executable" | 'options.forkOptions.executable = ""' | OperatingSystem.current().getExecutableName('/bin/javac') - } - - @Issue("https://github.com/gradle/gradle/issues/22397") - def "uses source and target compatibility from earlier toolchain from forkOptions #forkOption"() { - def currentJdk = Jvm.current() - def earlierJdk = AvailableJavaHomes.getDifferentVersion { it.languageVersion < currentJdk.javaVersion } - assumeNotNull(earlierJdk) - - def path = TextUtil.normaliseFileSeparators(earlierJdk.javaHome.absolutePath.toString() + appendPath) - - buildFile << """ - apply plugin: "java" - - compileJava { - options.fork = true - ${configure.replace("", path)} - - doFirst { - println "sourceCompatibility: \${sourceCompatibility}" - println "targetCompatibility: \${targetCompatibility}" - } - } - """ - - when: - withInstallations(earlierJdk).run(":compileJava", "--info") - - then: - executedAndNotSkipped(":compileJava") - outputContains("Compiling with toolchain '${earlierJdk.javaHome.absolutePath}'") - outputContains("sourceCompatibility: ${earlierJdk.javaVersion}") - outputContains("targetCompatibility: ${earlierJdk.javaVersion}") - earlierJdk.javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(compatibilityVersion) where: forkOption | configure | appendPath @@ -305,6 +268,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem outputContains("Compiling with JDK Java compiler API") outputDoesNotContain("Compiling with Java command line compiler") outputDoesNotContain("Started Gradle worker daemon") + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(curJvm.javaVersion) where: forkOption | configure | appendPath @@ -353,37 +317,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: outputContains("Compiling with Java command line compiler") outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'.") - javaClassFile("Foo.class").exists() - jdk.javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) - } - - def "uses matching compatibility options for source and target level"() { - def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) - buildFile << """ - apply plugin: "java" - - java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } - } - """ - - file("src/main/java/Bar.java") << """ - public class Bar { - public void bar() { - java.util.function.Function append = (var string) -> string + " "; - } - } - """ - - when: - withInstallations(jdk).run(":compileJava", "--info") - - then: - outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'.") - javaClassFile("Bar.class").exists() - jdk.javaVersion == JavaVersion.forClass(javaClassFile("Bar.class").bytes) + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(jdk.javaVersion) } def "uses correct vendor when selecting a toolchain"() { @@ -405,8 +339,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'.") - javaClassFile("Foo.class").exists() - jdk.javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(jdk.javaVersion) } @ToBeFixedForConfigurationCache(because = "Creates a second exception") @@ -448,130 +381,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: outputDoesNotContain("Compiling with Java command line compiler") outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'.") - javaClassFile("Foo.class").exists() - jdk.javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) - } - - def 'configuring toolchain on java extension with source and target compatibility is supported'() { - def jdk = Jvm.current() - def prevJavaVersion = JavaVersion.toVersion(jdk.javaVersion.majorVersion.toInteger() - 1) - buildFile << """ - apply plugin: 'java' - - java { - sourceCompatibility = JavaVersion.toVersion('$prevJavaVersion') - targetCompatibility = JavaVersion.toVersion('$prevJavaVersion') - toolchain { - languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) - } - } - - compileJava { - def projectSourceCompat = project.java.sourceCompatibility - def projectTargetCompat = project.java.targetCompatibility - doLast { - logger.lifecycle("project.sourceCompatibility = \$projectSourceCompat") - logger.lifecycle("project.targetCompatibility = \$projectTargetCompat") - logger.lifecycle("task.sourceCompatibility = \$sourceCompatibility") - logger.lifecycle("task.targetCompatibility = \$targetCompatibility") - } - } - """ - - when: - withInstallations(jdk).run(":compileJava") - - then: - outputContains("project.sourceCompatibility = $prevJavaVersion") - outputContains("project.targetCompatibility = $prevJavaVersion") - outputContains("task.sourceCompatibility = $prevJavaVersion") - outputContains("task.targetCompatibility = $prevJavaVersion") - prevJavaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) - } - - def 'configuring toolchain on java extension and clearing source and target compatibility is supported'() { - def jdk = Jvm.current() - def javaVersion = jdk.javaVersion - - buildFile << """ - apply plugin: 'java' - - java { - sourceCompatibility = JavaVersion.VERSION_14 - targetCompatibility = JavaVersion.VERSION_14 - toolchain { - languageVersion = JavaLanguageVersion.of(${javaVersion.majorVersion}) - } - sourceCompatibility = null - targetCompatibility = null - } - - compileJava { - def projectSourceCompat = project.java.sourceCompatibility - def projectTargetCompat = project.java.targetCompatibility - doLast { - logger.lifecycle("project.sourceCompatibility = \$projectSourceCompat") - logger.lifecycle("project.targetCompatibility = \$projectTargetCompat") - logger.lifecycle("task.sourceCompatibility = \$sourceCompatibility") - logger.lifecycle("task.targetCompatibility = \$targetCompatibility") - } - } - """ - - when: - withInstallations(jdk).run(":compileJava") - - then: - outputContains("project.sourceCompatibility = $javaVersion") - outputContains("project.targetCompatibility = $javaVersion") - outputContains("task.sourceCompatibility = $javaVersion") - outputContains("task.targetCompatibility = $javaVersion") - javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) - } - - def 'source and target compatibility override toolchain (source #source, target #target)'() { - def jdk11 = AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) - - buildFile << """ - apply plugin: 'java' - - java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } - } - - compileJava { - if ("$source" != 'none') - sourceCompatibility = JavaVersion.toVersion($source) - if ("$target" != 'none') - targetCompatibility = JavaVersion.toVersion($target) - def projectSourceCompat = project.java.sourceCompatibility - def projectTargetCompat = project.java.targetCompatibility - doLast { - logger.lifecycle("project.sourceCompatibility = \$projectSourceCompat") - logger.lifecycle("project.targetCompatibility = \$projectTargetCompat") - logger.lifecycle("task.sourceCompatibility = \$sourceCompatibility") - logger.lifecycle("task.targetCompatibility = \$targetCompatibility") - } - } - """ - - when: - withInstallations(jdk11).run(":compileJava") - - then: - outputContains("project.sourceCompatibility = 11") - outputContains("project.targetCompatibility = 11") - outputContains("task.sourceCompatibility = $sourceOut") - outputContains("task.targetCompatibility = $targetOut") - JavaVersion.toVersion(targetOut) == JavaVersion.forClass(javaClassFile("Foo.class").bytes) - - where: - source | target | sourceOut | targetOut - '9' | '10' | '9' | '10' - '9' | 'none' | '9' | '9' - 'none' | 'none' | '11' | '11' + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(jdk.javaVersion) } def "can compile Java using different JDKs"() { @@ -582,6 +392,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem plugins { id("java") } + java { toolchain { languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) @@ -595,8 +406,7 @@ class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implem then: outputDoesNotContain("Compiling with Java command line compiler") outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'.") - javaClassFile("Foo.class").exists() - jdk.javaVersion == JavaVersion.forClass(javaClassFile("Foo.class").bytes) + classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(jdk.javaVersion) where: javaVersion << JavaVersion.values().findAll { it.isJava8Compatible() } 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 a4f06a42139f..d7f919003858 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 @@ -519,7 +519,7 @@ public DirectoryProperty getGeneratedSourceOutputDirectory() { * * @since 4.3 * - * @deprecated Use {@link #getGeneratedSourceOutputDirectory()} instead. This method will be removed in Gradle 8.0. + * @deprecated Use {@link #getGeneratedSourceOutputDirectory()} instead. This method will be removed in Gradle 9.0. */ @Nullable @Deprecated @@ -527,7 +527,7 @@ public DirectoryProperty getGeneratedSourceOutputDirectory() { public File getAnnotationProcessorGeneratedSourcesDirectory() { DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory") .replaceWith("generatedSourceOutputDirectory") - .willBeRemovedInGradle8() + .willBeRemovedInGradle9() .withDslReference() .nagUser(); @@ -543,12 +543,12 @@ public File getAnnotationProcessorGeneratedSourcesDirectory() { */ @Deprecated public void setAnnotationProcessorGeneratedSourcesDirectory(@Nullable File file) { - // Used by Android plugin. Followup with https://github.com/gradle/gradle/issues/16782 - /*DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory") - .replaceWith("generatedSourceOutputDirectory") - .willBeRemovedInGradle8() - .withDslReference() - .nagUser();*/ + // Enable this deprecation in 8.1+. See: https://github.com/gradle/gradle/issues/16782 +// DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory") +// .replaceWith("generatedSourceOutputDirectory") +// .willBeRemovedInGradle9() +// .withDslReference() +// .nagUser(); this.generatedSourceOutputDirectory.set(file); } @@ -559,6 +559,13 @@ public void setAnnotationProcessorGeneratedSourcesDirectory(@Nullable File file) * @since 4.3 */ public void setAnnotationProcessorGeneratedSourcesDirectory(Provider file) { + // Enable this deprecation in 8.1+. +// 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/ForkOptions.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/ForkOptions.java index 8912afa83da6..a03d33c4b32f 100644 --- a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/ForkOptions.java +++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/ForkOptions.java @@ -36,11 +36,11 @@ public abstract class ForkOptions extends ProviderAwareCompilerDaemonForkOptions private File javaHome; /** - * Returns the compiler executable to be used. If set, - * a new compiler process will be forked for every compile task. - * Defaults to {@code null}. - * - *

      Setting the executable disables task output caching.

      + * Returns the compiler executable to be used. + *

      + * Only takes effect if {@code CompileOptions.fork} is {@code true}. Defaults to {@code null}. + *

      + * Setting the executable disables task output caching. */ @Nullable @Optional @@ -50,11 +50,11 @@ public String getExecutable() { } /** - * Sets the compiler executable to be used. If set, - * a new compiler process will be forked for every compile task. - * Defaults to {@code null}. - * - *

      Setting the executable disables task output caching.

      + * Sets the compiler executable to be used. + *

      + * Only takes effect if {@code CompileOptions.fork} is {@code true}. Defaults to {@code null}. + *

      + * Setting the executable disables task output caching. */ public void setExecutable(@Nullable String executable) { this.executable = executable; @@ -62,8 +62,8 @@ public void setExecutable(@Nullable String executable) { /** * Returns the Java home which contains the compiler to use. - * If set, a new compiler process will be forked for every compile task. - * Defaults to {@code null}. + *

      + * Only takes effect if {@code CompileOptions.fork} is {@code true}. Defaults to {@code null}. * * @since 3.5 */ @@ -75,8 +75,8 @@ public File getJavaHome() { /** * Sets the Java home which contains the compiler to use. - * If set, a new compiler process will be forked for every compile task. - * Defaults to {@code null}. + *

      + * Only takes effect if {@code CompileOptions.fork} is {@code true}. Defaults to {@code null}. * * @since 3.5 */ 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 16b02c62e23f..56d2d7eeafdd 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 @@ -149,7 +149,7 @@ class NativeLanguageSamplesIntegrationTest extends AbstractInstalledToolChainInt when: // To get rid of the deprecation, the sample under test could be split into two or otherwise refactored to use a single build file // Since this one uses Software Model + Windows, for now letting it stay with the deprecation - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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.usingBuildScript(windowsResources.dir.file('build-resource-only-dll.gradle')) run "helloResSharedLibrary" diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy index 60039944b286..27603e34ae2a 100644 --- a/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy +++ b/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy @@ -20,6 +20,7 @@ import org.gradle.api.logging.configuration.WarningMode import org.gradle.integtests.fixtures.AbstractIntegrationSpec import org.gradle.internal.featurelifecycle.LoggingDeprecatedFeatureHandler import org.gradle.util.internal.DefaultGradleVersion +import org.gradle.util.internal.ToBeImplemented class DeprecationHandlingIntegrationTest extends AbstractIntegrationSpec { public static final String PLUGIN_DEPRECATION_MESSAGE = 'The DeprecatedPlugin plugin has been deprecated' @@ -159,6 +160,7 @@ class DeprecationHandlingIntegrationTest extends AbstractIntegrationSpec { failure.assertHasDescription('Deprecated Gradle features were used in this build') } + @ToBeImplemented("Should only generate one deprecation warning but generates two") def 'DeprecatedPlugin from init script - without full stacktrace.'() { given: def initScript = file("init.gradle") << """ @@ -168,17 +170,18 @@ class DeprecationHandlingIntegrationTest extends AbstractIntegrationSpec { """.stripIndent() when: - executer.expectDeprecationWarning() + // TODO - should be 1, but deprecations are generated for both the main build and buildSrc + executer.expectDeprecationWarnings(2) executer.usingInitScript(initScript) run '-s' then: output.contains('init.gradle:3)') - output.count(PLUGIN_DEPRECATION_MESSAGE) == 1 + output.count(PLUGIN_DEPRECATION_MESSAGE) == 2 - output.count('\tat') == 1 - output.count('(Run with --stacktrace to get the full stack trace of this deprecation warning.)') == 1 + output.count('\tat') == 2 + output.count('(Run with --stacktrace to get the full stack trace of this deprecation warning.)') == 2 } def 'DeprecatedPlugin from applied script - #scenario'() { diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/LoggingIntegrationTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/LoggingIntegrationTest.groovy index 533ce17ad29e..92eaad8c7aae 100644 --- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/LoggingIntegrationTest.groovy +++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/LoggingIntegrationTest.groovy @@ -40,7 +40,8 @@ class LoggingIntegrationTest extends AbstractIntegrationTest { 'quietProject2ScriptClassPathOut', 'quietProject2CallbackOut', 'settings quiet out', - 'init QUIET out', + 'init : QUIET out', + 'init :buildSrc QUIET out', 'init callback quiet out', 'main buildSrc quiet', 'nestedBuild buildSrc quiet', @@ -53,7 +54,8 @@ class LoggingIntegrationTest extends AbstractIntegrationTest { 'external ERROR error message', '[ant:echo] An error message logged from Ant', 'A severe log message logged using JUL', - 'init ERROR err' + 'init : ERROR err', + 'init :buildSrc ERROR err' ) warning( 'A warning log message.', @@ -66,7 +68,8 @@ class LoggingIntegrationTest extends AbstractIntegrationTest { 'An error message which is logged at LIFECYCLE level', 'A task message which is logged at LIFECYCLE level', 'settings lifecycle log', - 'init lifecycle log', + 'init : lifecycle log', + 'init :buildSrc lifecycle log', 'external LIFECYCLE error message', 'external LIFECYCLE log message', 'LOGGER: evaluating :', @@ -92,9 +95,10 @@ class LoggingIntegrationTest extends AbstractIntegrationTest { 'infoProject2ScriptClassPathOut', 'settings info out', 'settings info log', - 'init INFO out', - 'init INFO err', - 'init info log', + 'init : INFO out', + 'init :buildSrc INFO out', + 'init :buildSrc INFO err', + 'init :buildSrc info log', 'LOGGER: build finished', 'LOGGER: evaluated project :', 'LOGGER: evaluated project :project1', diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/AbstractConsoleBuildPhaseFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/AbstractConsoleBuildPhaseFunctionalTest.groovy index cadbfb5ad548..0f8111e8bad7 100644 --- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/AbstractConsoleBuildPhaseFunctionalTest.groovy +++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/AbstractConsoleBuildPhaseFunctionalTest.groovy @@ -227,7 +227,7 @@ abstract class AbstractConsoleBuildPhaseFunctionalTest extends AbstractConsoleGr """ file("buildSrc/build.gradle") << """ ${server.callFromBuild('buildsrc-build-script')} - assemble { + jar { dependsOn { // call during task graph calculation ${server.callFromBuild('buildsrc-task-graph')} @@ -237,9 +237,6 @@ abstract class AbstractConsoleBuildPhaseFunctionalTest extends AbstractConsoleGr ${server.callFromBuild('buildsrc-task')} } } - gradle.buildFinished { - ${server.callFromBuild('buildsrc-build-finished')} - } """ given: @@ -247,7 +244,6 @@ abstract class AbstractConsoleBuildPhaseFunctionalTest extends AbstractConsoleGr def childBuildScript = server.expectAndBlock('buildsrc-build-script') def childTaskGraph = server.expectAndBlock('buildsrc-task-graph') def task1 = server.expectAndBlock('buildsrc-task') - def childBuildFinished = server.expectAndBlock('buildsrc-build-finished') def rootBuildScript = server.expectAndBlock('root-build-script') def task2 = server.expectAndBlock('task2') def rootBuildFinished = server.expectAndBlock('root-build-finished') @@ -273,11 +269,6 @@ abstract class AbstractConsoleBuildPhaseFunctionalTest extends AbstractConsoleGr assertHasBuildPhase("0% INITIALIZING") task1.releaseAll() - and: - childBuildFinished.waitForAllPendingCalls() - assertHasBuildPhase("0% INITIALIZING") - childBuildFinished.releaseAll() - and: rootBuildScript.waitForAllPendingCalls() assertHasBuildPhase("75% CONFIGURING") diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleBuildSrcGroupedTaskFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleBuildSrcGroupedTaskFunctionalTest.groovy index b9b1e07d8c65..204554a2a024 100644 --- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleBuildSrcGroupedTaskFunctionalTest.groovy +++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleBuildSrcGroupedTaskFunctionalTest.groovy @@ -30,8 +30,8 @@ abstract class AbstractConsoleBuildSrcGroupedTaskFunctionalTest extends Abstract logger.quiet '$HELLO_WORLD_MESSAGE' } } - - assemble.dependsOn helloWorld + + jar.dependsOn helloWorld """ buildFile << """ task byeWorld { diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleGradleBuildGroupedTaskFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleGradleBuildGroupedTaskFunctionalTest.groovy index 0b97fbe3e588..c63b7d0eb346 100644 --- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleGradleBuildGroupedTaskFunctionalTest.groovy +++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/taskgrouping/AbstractConsoleGradleBuildGroupedTaskFunctionalTest.groovy @@ -32,7 +32,7 @@ abstract class AbstractConsoleGradleBuildGroupedTaskFunctionalTest extends Abstr file(externalBuildScriptPath) << externalBuildScript() when: - executer.expectDocumentedDeprecationWarning("Specifying custom build file location has been deprecated. This is scheduled to be removed in Gradle 8.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 build 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") succeeds(AGGREGATE_TASK_NAME) then: diff --git a/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/init.gradle b/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/init.gradle index f6042a01dda8..40260cf2c9de 100644 --- a/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/init.gradle +++ b/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/init.gradle @@ -1,16 +1,19 @@ +def prefix = "init $gradle.identityPath" -println 'init QUIET out' +println "$prefix QUIET out" logging.captureStandardOutput LogLevel.INFO -println 'init INFO out' +println "$prefix INFO out" -System.err.println 'init ERROR err' +System.err.println "$prefix ERROR err" logging.captureStandardError LogLevel.INFO -System.err.println 'init INFO err' +System.err.println "$prefix INFO err" -logger.lifecycle('init lifecycle log') -logger.info('init info log') +logger.lifecycle("$prefix lifecycle log") +logger.info("$prefix info log") -useLogger(new CustomLogger()) +if (gradle.parent == null) { + useLogger(new CustomLogger()) +} class CustomLogger extends BuildAdapter implements BuildListener, ProjectEvaluationListener, TaskExecutionListener, TaskActionListener { def logger = Logging.getLogger('init-script') @@ -43,4 +46,4 @@ class CustomLogger extends BuildAdapter implements BuildListener, ProjectEvaluat public void afterActions(Task task) { logger.info("LOGGER: task $task.path completed work") } -} \ No newline at end of file +} diff --git a/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecationMessageBuilder.java b/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecationMessageBuilder.java index bda006e89674..c3fde1a8643e 100644 --- a/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecationMessageBuilder.java +++ b/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecationMessageBuilder.java @@ -50,14 +50,6 @@ public T withAdvice(String advice) { return (T) this; } - /** - * Output: This is scheduled to be removed in Gradle 8.0. - */ - public WithDeprecationTimeline willBeRemovedInGradle8() { - this.deprecationTimeline = DeprecationTimeline.willBeRemovedInVersion(GRADLE8); - return new WithDeprecationTimeline(this); - } - /** * Output: This will fail with an error in Gradle 8.0. */ @@ -66,14 +58,6 @@ public WithDeprecationTimeline willBecomeAnErrorInGradle8() { return new WithDeprecationTimeline(this); } - /** - * Output: This will change in Gradle 8.0. - */ - public WithDeprecationTimeline willChangeInGradle8() { - this.deprecationTimeline = DeprecationTimeline.willChangeInVersion(GRADLE8); - return new WithDeprecationTimeline(this); - } - /** * Output: This is scheduled to be removed in Gradle 9.0. */ @@ -242,12 +226,6 @@ public static class DeprecateProperty extends WithReplacement { + @Nonnull + @Override + DecompressionCache create(); +} 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 new file mode 100644 index 000000000000..6a90d0a5b881 --- /dev/null +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java @@ -0,0 +1,66 @@ +/* + * 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.cache.internal; + +import com.google.common.annotations.VisibleForTesting; +import org.gradle.cache.FileLockManager; +import org.gradle.cache.PersistentCache; +import org.gradle.cache.scopes.ScopedCache; + +import java.io.File; + +import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; + +/** + * The default implementation of {@link DecompressionCache} that can be used to store decompressed data extracted from archive files like zip and tars. + * + * Will manage access to the cache, so that access to the archive's contents + * are only permitted to one client at a time. The cache will be a Gradle cross version cache. + */ +public class DefaultDecompressionCache implements DecompressionCache { + private static final String EXPANSION_CACHE_KEY = "expanded"; + private static final String EXPANSION_CACHE_NAME = "Compressed Files Expansion Cache"; + + private final PersistentCache cache; + + public DefaultDecompressionCache(ScopedCache cacheFactory) { + this.cache = cacheFactory.crossVersionCache(EXPANSION_CACHE_KEY) + .withDisplayName(EXPANSION_CACHE_NAME) + .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) + .open(); + } + + @VisibleForTesting + public DefaultDecompressionCache(PersistentCache cache) { + this.cache = cache; + } + + @Override + public void useCache(Runnable action) { + cache.useCache(action); + } + + @Override + public File getBaseDir() { + return cache.getBaseDir(); + } + + @Override + public void close() { + cache.close(); + } +} diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/scopes/DefaultProjectScopedCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/scopes/DefaultProjectScopedCache.java new file mode 100644 index 000000000000..b37d6e1b71dc --- /dev/null +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/scopes/DefaultProjectScopedCache.java @@ -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.cache.internal.scopes; + +import org.gradle.cache.CacheRepository; +import org.gradle.cache.scopes.ProjectScopedCache; + +import java.io.File; + +/** + * Default implementation of {@link ProjectScopedCache}, implements interface using {@link AbstractScopedCache}. + */ +public class DefaultProjectScopedCache extends AbstractScopedCache implements ProjectScopedCache { + public DefaultProjectScopedCache(File rootDir, CacheRepository cacheRepository) { + super(rootDir, cacheRepository); + } +} diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ProjectScopedCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ProjectScopedCache.java new file mode 100644 index 000000000000..8ce3b4f9dde7 --- /dev/null +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ProjectScopedCache.java @@ -0,0 +1,27 @@ +/* + * 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.cache.scopes; + +import org.gradle.internal.service.scopes.Scopes; +import org.gradle.internal.service.scopes.ServiceScope; + +/** + * Factory for creating project scoped caches. These typically live under the ~/build directory of a project. + */ +@ServiceScope(Scopes.Project.class) +public interface ProjectScopedCache extends ScopedCache { +} diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ScopedCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ScopedCache.java index 4afd46f9ad8b..5a1bb5f98224 100644 --- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ScopedCache.java +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ScopedCache.java @@ -41,12 +41,12 @@ public interface ScopedCache { File getRootDir(); /** - * Returns the base directory that would be used for a Gradle-version specific cache om this scope. + * Returns the base directory that would be used for a Gradle-version specific cache of this scope. */ File baseDirForCache(String key); /** - * Returns the base directory that would be used for a cross Gradle version specific cache om this scope. + * Returns the base directory that would be used for a cross Gradle version specific cache of this scope. */ File baseDirForCrossVersionCache(String key); } diff --git a/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginConvention.java b/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginConvention.java index 43db7718b8fe..8829a6e07a47 100644 --- a/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginConvention.java +++ b/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginConvention.java @@ -21,7 +21,7 @@ /** *

      A {@link Convention} used for the BasePlugin.

      * - * @deprecated Use {@link BasePluginExtension} instead. This class is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link BasePluginExtension} instead. This class is scheduled for removal in Gradle 9.0. */ @Deprecated public abstract class BasePluginConvention { diff --git a/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginExtension.java b/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginExtension.java index 53cf8ca19f47..5c740be4a417 100644 --- a/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginExtension.java +++ b/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePluginExtension.java @@ -49,7 +49,7 @@ public interface BasePluginExtension { /** * This method is only here to maintain compatibility with existing builds. * - * @deprecated Use {@link #getDistsDirectory()}. This method is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link #getDistsDirectory()}. This method is scheduled for removal in Gradle 9.0. */ @Deprecated String getDistsDirName(); @@ -57,7 +57,7 @@ public interface BasePluginExtension { /** * This method is only here to maintain compatibility with existing builds. * - * @deprecated Use {@link #getDistsDirectory()}. This method is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link #getDistsDirectory()}. This method is scheduled for removal in Gradle 9.0. */ @Deprecated void setDistsDirName(String distsDirName); @@ -65,7 +65,7 @@ public interface BasePluginExtension { /** * This method is only here to maintain compatibility with existing builds. * - * @deprecated Use {@link #getLibsDirectory()}. This method is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link #getLibsDirectory()}. This method is scheduled for removal in Gradle 9.0. */ @Deprecated String getLibsDirName(); @@ -73,7 +73,7 @@ public interface BasePluginExtension { /** * This method is only here to maintain compatibility with existing builds. * - * @deprecated Use {@link #getLibsDirectory()}. This method is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link #getLibsDirectory()}. This method is scheduled for removal in Gradle 9.0. */ @Deprecated void setLibsDirName(String libsDirName); @@ -81,7 +81,7 @@ public interface BasePluginExtension { /** * This method is only here to maintain compatibility with existing builds. * - * @deprecated Use {@link #getArchivesName()}. This method is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link #getArchivesName()}. This method is scheduled for removal in Gradle 9.0. */ @Deprecated String getArchivesBaseName(); @@ -89,7 +89,7 @@ public interface BasePluginExtension { /** * This method is only here to maintain compatibility with existing builds. * - * @deprecated Use {@link #getArchivesName()}. This method is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link #getArchivesName()}. This method is scheduled for removal in Gradle 9.0. */ @Deprecated void setArchivesBaseName(String archivesBaseName); diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsCompatibilityIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsCompatibilityIntegrationTest.groovy index 80af62064140..34647ca1ea5d 100644 --- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsCompatibilityIntegrationTest.groovy +++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsCompatibilityIntegrationTest.groovy @@ -60,7 +60,7 @@ class GradleImplDepsCompatibilityIntegrationTest extends BaseGradleImplDepsInteg def "Gradle API dependency does not contain any of TestKit dependency classes and vice versa"() { given: - def outputDirName = 'build' + def outputDirPath = 'build/output' buildFile << """ configurations { gradleApi @@ -75,7 +75,7 @@ class GradleImplDepsCompatibilityIntegrationTest extends BaseGradleImplDepsInteg task resolveDependencyArtifacts { doLast { copy { - into '$outputDirName' + into '$outputDirPath' from configurations.gradleApi.resolve().find { it.name.startsWith('gradle-api-') } from configurations.testKit.resolve().find { it.name.startsWith('gradle-test-kit-') } } @@ -87,13 +87,13 @@ class GradleImplDepsCompatibilityIntegrationTest extends BaseGradleImplDepsInteg succeeds 'resolveDependencyArtifacts' then: - def outputDir = temporaryFolder.testDirectory.file(outputDirName) + def outputDir = temporaryFolder.testDirectory.file(outputDirPath) def jarFiles = outputDir.listFiles() jarFiles.size() == 2 def gradleApiJar = jarFiles.find { it.name.startsWith('gradle-api-') } def testKitJar = jarFiles.find { it.name.startsWith('gradle-test-kit-') } - File gradleApiClassNamesTextFile = temporaryFolder.testDirectory.file("$outputDirName/gradle-api-classes.txt") - File testKitClassNamesTextFile = temporaryFolder.testDirectory.file("$outputDirName/testkit-classes.txt") + File gradleApiClassNamesTextFile = temporaryFolder.testDirectory.file("$outputDirPath/gradle-api-classes.txt") + File testKitClassNamesTextFile = temporaryFolder.testDirectory.file("$outputDirPath/testkit-classes.txt") writeClassesInZipFileToTextFile(gradleApiJar, gradleApiClassNamesTextFile) writeClassesInZipFileToTextFile(testKitJar, testKitClassNamesTextFile) assert gradleApiClassNamesTextFile.size() > 0 diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java index 5cea78bc2596..e3971476adde 100644 --- a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java +++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginRequestApplicator.java @@ -28,7 +28,6 @@ import org.gradle.api.internal.plugins.PluginRegistry; import org.gradle.api.plugins.InvalidPluginException; import org.gradle.api.plugins.UnknownPluginException; -import org.gradle.internal.classpath.CachedClasspathTransformer; import org.gradle.internal.classpath.ClassPath; import org.gradle.internal.event.ListenerManager; import org.gradle.internal.exceptions.LocationAwareException; @@ -56,7 +55,6 @@ import static com.google.common.collect.Lists.newLinkedList; import static com.google.common.collect.Maps.newLinkedHashMap; -import static org.gradle.internal.classpath.CachedClasspathTransformer.StandardTransform.BuildLogic; import static org.gradle.util.internal.CollectionUtils.collect; public class DefaultPluginRequestApplicator implements PluginRequestApplicator { @@ -65,7 +63,6 @@ public class DefaultPluginRequestApplicator implements PluginRequestApplicator { private final PluginArtifactRepositoriesProvider pluginRepositoriesProvider; private final PluginResolutionStrategyInternal pluginResolutionStrategy; private final PluginInspector pluginInspector; - private final CachedClasspathTransformer cachedClasspathTransformer; private final PluginVersionTracker pluginVersionTracker; private final PluginApplicationListener pluginApplicationListenerBroadcaster; @@ -75,7 +72,6 @@ public DefaultPluginRequestApplicator( PluginArtifactRepositoriesProvider pluginRepositoriesProvider, PluginResolutionStrategyInternal pluginResolutionStrategy, PluginInspector pluginInspector, - CachedClasspathTransformer cachedClasspathTransformer, PluginVersionTracker pluginVersionTracker, ListenerManager listenerManager ) { @@ -84,7 +80,6 @@ public DefaultPluginRequestApplicator( this.pluginRepositoriesProvider = pluginRepositoriesProvider; this.pluginResolutionStrategy = pluginResolutionStrategy; this.pluginInspector = pluginInspector; - this.cachedClasspathTransformer = cachedClasspathTransformer; this.pluginVersionTracker = pluginVersionTracker; this.pluginApplicationListenerBroadcaster = listenerManager.getBroadcaster(PluginApplicationListener.class); } @@ -178,7 +173,7 @@ private void addPluginArtifactRepositories(PluginArtifactRepositories resolveCon } private void defineScriptHandlerClassScope(ScriptHandlerInternal scriptHandler, ClassLoaderScope classLoaderScope, Iterable> pluginsFromOtherLoaders) { - exportBuildLogicClassPathTo(classLoaderScope, scriptHandler.getNonInstrumentedScriptClassPath()); + exportBuildLogicClassPathTo(classLoaderScope, scriptHandler.getInstrumentedScriptClassPath()); for (PluginImplementation pluginImplementation : pluginsFromOtherLoaders) { classLoaderScope.export(pluginImplementation.asClass().getClassLoader()); @@ -188,8 +183,7 @@ private void defineScriptHandlerClassScope(ScriptHandlerInternal scriptHandler, } private void exportBuildLogicClassPathTo(ClassLoaderScope classLoaderScope, ClassPath classPath) { - ClassPath cachedClassPath = cachedClasspathTransformer.transform(classPath, BuildLogic); - classLoaderScope.export(cachedClassPath); + classLoaderScope.export(classPath); } private PluginResolver wrapInAlreadyInClasspathResolver(ClassLoaderScope classLoaderScope, PluginArtifactRepositories resolveContext) { diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BuildSrcPluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BuildSrcPluginIntegrationTest.groovy index 2797cfd3444f..010a15e229a0 100644 --- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BuildSrcPluginIntegrationTest.groovy +++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BuildSrcPluginIntegrationTest.groovy @@ -187,8 +187,7 @@ class BuildSrcPluginIntegrationTest extends AbstractIntegrationSpec { } """ then: - succeeds "help" - outputContains("Task :buildSrc:subInBuildSrc:compileJava") + succeeds ":buildSrc:subInBuildSrc:assemble" } private void writeBuildSrcPlugin(String location, String className) { diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaBasePluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaBasePluginIntegrationTest.groovy index 3c1e0ddd8a9e..ba75ad304d5e 100644 --- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaBasePluginIntegrationTest.groovy +++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaBasePluginIntegrationTest.groovy @@ -16,14 +16,9 @@ package org.gradle.api.plugins -import org.gradle.api.JavaVersion 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 org.gradle.util.internal.TextUtil -class JavaBasePluginIntegrationTest extends AbstractIntegrationSpec implements JavaToolchainFixture { +class JavaBasePluginIntegrationTest extends AbstractIntegrationSpec { def "can define and build a source set with implementation dependencies"() { settingsFile << """ @@ -52,116 +47,4 @@ class JavaBasePluginIntegrationTest extends AbstractIntegrationSpec implements J file("main/build/classes/java/main").assertHasDescendants("Main.class") file("tests/build/classes/java/unitTest").assertHasDescendants("Test.class") } - - def "can configure source and target Java versions"() { - def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_1_8) - buildFile << """ - apply plugin: 'java-base' - java { - sourceCompatibility = JavaVersion.VERSION_1_7 - targetCompatibility = JavaVersion.VERSION_1_8 - } - sourceSets { - unitTest { } - } - compileUnitTestJava { - options.fork = true - options.forkOptions.javaHome = file("${TextUtil.normaliseFileSeparators(jdk.javaHome.toString())}") - } - compileUnitTestJava.doFirst { - assert sourceCompatibility == "1.7" - assert targetCompatibility == "1.8" - } - """ - file("src/unitTest/java/Test.java") << """public class Test { }""" - - expect: - succeeds("unitTestClasses") - } - - def "source compatibility convention is set and used for target compatibility convention"() { - def jdk11 = AvailableJavaHomes.getJdk11() - - buildFile << """ - apply plugin: 'java-base' - - java { - ${extensionSource != null ? "sourceCompatibility = JavaVersion.toVersion('$extensionSource')" : ""} - } - - sourceSets { - customCompile { } - } - - compileCustomCompileJava { - ${taskSource != null ? "sourceCompatibility = '$taskSource'" : ""} - ${taskRelease != null ? "options.release = $taskRelease" : ""} - ${taskToolchain != null ? "javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of($taskToolchain) }" : ""} - } - - compileCustomCompileJava.doFirst { - assert sourceCompatibility == "${sourceOut}" - assert targetCompatibility == "${targetOut}" - } - """ - - file("src/customCompile/java/Test.java") << """public class Test { }""" - - expect: - withInstallations(jdk11).succeeds("customCompileClasses") - - where: - taskSource | taskRelease | extensionSource | taskToolchain | sourceOut | targetOut - "9" | "11" | "11" | "11" | "9" | "11" // target differs because release is set - null | "9" | "11" | "11" | "9" | "9" - null | null | "9" | "11" | "9" | "9" - null | null | null | "11" | "11" | "11" - null | null | null | null | currentJavaVersion() | currentJavaVersion() - } - - def "target compatibility convention is set"() { - def jdk11 = AvailableJavaHomes.getJdk11() - - buildFile << """ - apply plugin: 'java-base' - - java { - ${extensionTarget != null ? "targetCompatibility = JavaVersion.toVersion(${extensionTarget})" : ""} - } - - sourceSets { - customCompile { } - } - - compileCustomCompileJava { - ${taskTarget != null ? "targetCompatibility = '${taskTarget}'" : ""} - ${taskRelease ? "options.release = ${taskRelease}" : ""} - ${taskSource ? "sourceCompatibility = '${taskSource}'" : ""} - ${taskToolchain != null ? "javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(${taskToolchain}) }" : ""} - } - - compileCustomCompileJava.doFirst { - assert targetCompatibility == "${targetOut}" - assert sourceCompatibility == "${sourceOut}" - } - """ - - file("src/customCompile/java/Test.java") << """public class Test { }""" - - expect: - withInstallations(jdk11).succeeds("customCompileClasses") - - where: - taskTarget | taskRelease | extensionTarget | taskSource | taskToolchain | targetOut | sourceOut - "9" | "11" | "11" | "11" | "11" | "9" | "11" - null | "9" | "11" | "11" | "11" | "9" | "11" - null | null | "9" | "8" | "11" | "9" | "8" - null | null | null | "9" | "11" | "9" | "9" - null | null | null | null | "11" | "11" | "11" - null | null | null | null | null | currentJavaVersion() | currentJavaVersion() - } - - private static String currentJavaVersion() { - return Jvm.current().javaVersion.toString() - } } 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 a5e7b49717b7..073d6d386fba 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 @@ -22,7 +22,7 @@ /** *

      The {@link Convention} used for configuring the {@link ApplicationPlugin}.

      * - * @deprecated Use {@link JavaApplication} instead. This class is scheduled for removal in Gradle 8.0. + * @deprecated Use {@link JavaApplication} instead. This class is scheduled for removal in Gradle 9.0. */ @Deprecated public abstract class ApplicationPluginConvention { diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPluginConvention.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPluginConvention.java index 65b13379a443..0a9bafd53fe5 100644 --- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPluginConvention.java +++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPluginConvention.java @@ -30,7 +30,7 @@ * Is mixed into the project when applying the {@link org.gradle.api.plugins.JavaBasePlugin} or the * {@link org.gradle.api.plugins.JavaPlugin}. * - * @deprecated Replaced by {@link JavaPluginExtension}. This class is scheduled for removal in Gradle 8.0. + * @deprecated Replaced by {@link JavaPluginExtension}. This class is scheduled for removal in Gradle 9.0. */ @Deprecated public abstract class JavaPluginConvention { diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPluginConvention.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPluginConvention.java index 7d8bad85796f..1b6ac92c66a7 100644 --- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPluginConvention.java +++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPluginConvention.java @@ -23,7 +23,7 @@ /** *

      A {@link Convention} used for the WarPlugin.

      * - * @deprecated Please configure the tasks directly. This class is scheduled for removal in Gradle 8.0. + * @deprecated Please configure the tasks directly. This class is scheduled for removal in Gradle 9.0. */ @Deprecated public abstract class WarPluginConvention { diff --git a/subprojects/plugins/src/main/java/org/gradle/api/tasks/GroovySourceSet.java b/subprojects/plugins/src/main/java/org/gradle/api/tasks/GroovySourceSet.java index ab5b177c794c..296c8567217c 100644 --- a/subprojects/plugins/src/main/java/org/gradle/api/tasks/GroovySourceSet.java +++ b/subprojects/plugins/src/main/java/org/gradle/api/tasks/GroovySourceSet.java @@ -27,7 +27,7 @@ * org.gradle.api.plugins.GroovyPlugin}. * * @deprecated Using convention to contribute to source sets is deprecated. You can configure the groovy sources via the {@code GroovySourceDirectorySet} extension (e.g. - * {@code sourceSet.getExtensions().getByType(GroovySourceDirectorySet.class).setSrcDirs(...)}). This interface is scheduled for removal in Gradle 8.0. + * {@code sourceSet.getExtensions().getByType(GroovySourceDirectorySet.class).setSrcDirs(...)}). This interface is scheduled for removal in Gradle 9.0. */ @Deprecated public interface GroovySourceSet { diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/CreateStartScripts.java b/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/CreateStartScripts.java index f547f0c8d23e..79cbb86e0d78 100644 --- a/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/CreateStartScripts.java +++ b/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/CreateStartScripts.java @@ -257,7 +257,7 @@ public Property getMainClass() { public String getMainClassName() { DeprecationLogger.deprecateProperty(CreateStartScripts.class, "mainClassName") .replaceWith("mainClass") - .willBeRemovedInGradle8() + .willBeRemovedInGradle9() .withDslReference() .nagUser(); @@ -268,7 +268,7 @@ public String getMainClassName() { public void setMainClassName(@Nullable String mainClassName) { DeprecationLogger.deprecateProperty(CreateStartScripts.class, "mainClassName") .replaceWith("mainClass") - .willBeRemovedInGradle8() + .willBeRemovedInGradle9() .withDslReference() .nagUser(); 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 d76cdad8812b..fe6ce8a576e7 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 @@ -34,7 +34,7 @@ class SamplesJavaTestingIntegrationTest extends AbstractSampleIntegrationTest { @UsesSample("java/basic") def "can execute simple Java tests with #dsl dsl"() { given: - configureExecuterForToolchains('11') + configureExecuterForToolchains('17') TestFile dslDir = sample.dir.file(dsl) executer.inDirectory(dslDir) @@ -312,7 +312,7 @@ class SamplesJavaTestingIntegrationTest extends AbstractSampleIntegrationTest { @UsesSample("java/basic") def "can run simple Java integration tests with #dsl dsl"() { given: - configureExecuterForToolchains('11') + configureExecuterForToolchains('17') TestFile dslDir = sample.dir.file(dsl) executer.inDirectory(dslDir) @@ -335,7 +335,7 @@ class SamplesJavaTestingIntegrationTest extends AbstractSampleIntegrationTest { @UsesSample("java/basic") def "can skip the tests with an `onlyIf` condition with #dsl dsl"() { given: - configureExecuterForToolchains('11') + configureExecuterForToolchains('17') TestFile dslDir = sample.dir.file(dsl) when: "run first time to populate configuration cache if it is enabled" diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy index 8ac8467c2163..496f72775d2a 100644 --- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy +++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ZincScalaCompilerIntegrationTest.groovy @@ -73,11 +73,7 @@ class ZincScalaCompilerIntegrationTest extends BasicZincScalaCompilerIntegration } - @Issue("https://github.com/gradle/gradle/issues/22964") def "compiles Scala code incrementally"() { - // TODO: remove the assumption when the linked issue fixed for Scala 3 - Assume.assumeTrue(versionNumber.major == 2) - file("src/main/scala/Person.scala") << """class Person(val name: String = "foo", val age: Int = 1)""" file("src/main/scala/House.scala") << """class House(val owner: Person = new Person())""" file("src/main/scala/Other.scala") << """class Other""" @@ -144,11 +140,7 @@ class ZincScalaCompilerIntegrationTest extends BasicZincScalaCompilerIntegration other.lastModified() == old(other.lastModified()) } - @Issue("https://github.com/gradle/gradle/issues/22964") def "compiles Scala incrementally across project boundaries"() { - // TODO: remove the assumption when the linked issue fixed for Scala 3 - Assume.assumeTrue(versionNumber.major == 2) - file("settings.gradle") << """include 'a', 'b'""" // overwrite the build file from setup file("build.gradle").text = """ diff --git a/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java b/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java index 518c4aae5138..99a4ab31db9c 100644 --- a/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java +++ b/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompiler.java @@ -30,25 +30,16 @@ import org.gradle.language.base.internal.compile.Compiler; import org.gradle.util.internal.GFileUtils; import sbt.internal.inc.Analysis; -import sbt.internal.inc.ExternalLookup; import sbt.internal.inc.IncrementalCompilerImpl; import sbt.internal.inc.Locate; import sbt.internal.inc.LoggedReporter; import sbt.internal.inc.PlainVirtualFileConverter; import sbt.internal.inc.ScalaInstance; -import sbt.internal.inc.Stamper; import scala.Option; -import scala.Some; -import scala.collection.immutable.Set; -import scala.jdk.javaapi.CollectionConverters; import xsbti.T2; import xsbti.VirtualFile; -import xsbti.VirtualFileRef; -import xsbti.api.AnalyzedClass; import xsbti.compile.AnalysisContents; import xsbti.compile.AnalysisStore; -import xsbti.compile.Changes; -import xsbti.compile.ClassFileManager; import xsbti.compile.ClassFileManagerType; import xsbti.compile.ClasspathOptionsUtil; import xsbti.compile.CompileAnalysis; @@ -57,8 +48,6 @@ import xsbti.compile.CompilerCache; import xsbti.compile.Compilers; import xsbti.compile.DefinesClass; -import xsbti.compile.ExternalHooks; -import xsbti.compile.FileHash; import xsbti.compile.IncOptions; import xsbti.compile.Inputs; import xsbti.compile.PerClasspathEntryLookup; @@ -66,7 +55,6 @@ import xsbti.compile.ScalaCompiler; import xsbti.compile.Setup; import xsbti.compile.TransactionalManagerType; -import xsbti.compile.analysis.Stamp; import javax.inject.Inject; import java.io.File; @@ -138,7 +126,6 @@ public WorkResult execute(final ScalaJavaJointCompileSpec spec) { .orElse(PreviousResult.of(Optional.empty(), Optional.empty())); IncOptions incOptions = IncOptions.of() - .withExternalHooks(new LookupOnlyExternalHooks(new ExternalBinariesLookup())) .withRecompileOnMacroDef(Optional.of(false)) .withClassfileManagerType(classFileManagerType) .withTransitiveStep(5); @@ -211,81 +198,6 @@ public DefinesClass definesClass(VirtualFile classpathEntry) { } } - private static class ExternalBinariesLookup implements ExternalLookup { - - @Override - public Option lookupAnalyzedClass(String binaryClassName, Option file) { - return Option.empty(); - } - - @Override - public Option> changedSources(CompileAnalysis previousAnalysis) { - return Option.empty(); - } - - @Override - public Option> changedBinaries(CompileAnalysis previousAnalysis) { - List result = new java.util.ArrayList<>(); - - for (Map.Entry e : previousAnalysis.readStamps().getAllLibraryStamps().entrySet()) { - File path = CONVERTER.toPath(e.getKey()).toFile(); - if (!path.exists() || !e.getValue().equals(Stamper.forLastModifiedInRootPaths(CONVERTER).apply(e.getKey()))) { - result.add(e.getKey()); - } - } - if (result.isEmpty()) { - return Option.empty(); - } else { - return new Some<>(CollectionConverters.asScala(result).toSet()); - } - } - - - @Override - public Option> removedProducts(CompileAnalysis previousAnalysis) { - return Option.empty(); - } - - @Override - public boolean shouldDoIncrementalCompilation(Set changedClasses, CompileAnalysis analysis) { - return true; - } - - - @Override - public Optional hashClasspath(VirtualFile[] classpath) { - return Optional.empty(); - } - } - - private static class LookupOnlyExternalHooks implements ExternalHooks { - private final Optional lookup; - - public LookupOnlyExternalHooks(Lookup lookup) { - this.lookup = Optional.of(lookup); - } - - @Override - public Optional getExternalLookup() { - return lookup; - } - - @Override - public Optional getExternalClassFileManager() { - return Optional.empty(); - } - - @Override - public ExternalHooks withExternalClassFileManager(ClassFileManager externalClassFileManager) { - return this; - } - - @Override - public ExternalHooks withExternalLookup(Lookup externalLookup) { - return this; - } - } - private static class AnalysisBakedDefineClass implements DefinesClass { private final Analysis analysis; diff --git a/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerFacade.java b/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerFacade.java index 694776b1f485..e5c7745fa3ca 100644 --- a/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerFacade.java +++ b/subprojects/scala/src/main/java/org/gradle/api/internal/tasks/scala/ZincScalaCompilerFacade.java @@ -18,13 +18,7 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.cache.CacheRepository; -import org.gradle.cache.internal.CacheFactory; -import org.gradle.cache.internal.DefaultCacheRepository; -import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; -import org.gradle.initialization.GradleUserHomeDirProvider; -import org.gradle.initialization.layout.GlobalCacheDir; import org.gradle.language.base.internal.compile.Compiler; -import org.gradle.util.GradleVersion; import javax.inject.Inject; import java.io.Serializable; @@ -35,10 +29,8 @@ public class ZincScalaCompilerFacade implements Compiler connection.newBuild().addProgressListener(events).run() + withConnection { connection -> + def operation = connection.newBuild() + .addProgressListener(events) + collectOutputs(operation) + operation.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 1e57b70ddb19..b69c1f9d175e 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 @@ -191,7 +191,9 @@ class ProjectConfigurationChildrenProgressCrossVersionSpec extends AbstractProgr and: println events.describeOperationsTree() - events.operation(applyInitScript(initScript)).with { operation -> + def initScripts = events.operations(applyInitScript(initScript)) + !initScripts.empty // Root build, plus buildSrc for Gradle >=8.0 + initScripts.each { operation -> operation.child applyInitScriptPlugin(scriptPlugin1) operation.child applyInitScriptPlugin(scriptPlugin2) } diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/TaskDependenciesCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/TaskDependenciesCrossVersionSpec.groovy index 4d317bc5b6da..ef451eedf609 100644 --- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/TaskDependenciesCrossVersionSpec.groovy +++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/TaskDependenciesCrossVersionSpec.groovy @@ -58,7 +58,7 @@ class TaskDependenciesCrossVersionSpec extends ToolingApiSpecification { task b { dependsOn(a) } task c { dependsOn(b) } task d { dependsOn(b, c) } - build.dependsOn(d) + jar.dependsOn(d) """ when: diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/CrossVersionTestEngine.java b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/CrossVersionTestEngine.java index 8f67322e6894..8085db91c68c 100644 --- a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/CrossVersionTestEngine.java +++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/CrossVersionTestEngine.java @@ -292,7 +292,7 @@ public ToolingApiDistribution run(ToolingApiDistributionResolver resolver) { private String getToolingApiVersionToLoad() { String candidateTapiVersion = System.getProperty(VERSIONS_SYSPROP_NAME); - if (CoverageContext.LATEST.selector.equals(candidateTapiVersion)) { + if (CoverageContext.LATEST.selector.equals(candidateTapiVersion) || CoverageContext.PARTIAL.selector.equals(candidateTapiVersion)) { ReleasedVersionDistributions releasedVersions = new ReleasedVersionDistributions(IntegrationTestBuildContext.INSTANCE); return releasedVersions.getMostRecentRelease().getVersion().getVersion(); }