From 2d3deef592e730f8c71c1d3cf9770254e1e5ccef Mon Sep 17 00:00:00 2001 From: Guy Brand Date: Thu, 17 Nov 2022 23:34:52 +0100 Subject: [PATCH 001/120] Gradle Enterprise plugin degradation when using configuration cache When using a version of the Gradle Enterprise plugin < 3.12, and configuration cache is used, we'll disable the Gradle Enterprise plugin and show a warning message. --- ...erprisePluginCheckInIntegrationTest.groovy | 21 ++++- .../BuildScanAutoApplyIntegrationTest.groovy | 6 +- ...tGradleEnterprisePluginCheckInService.java | 89 +++++++++++++++++++ ...tGradleEnterprisePluginCheckInService.java | 78 ---------------- .../impl/GradleEnterprisePluginServices.java | 2 +- 5 files changed, 111 insertions(+), 85 deletions(-) create mode 100644 subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java delete mode 100644 subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefautGradleEnterprisePluginCheckInService.java 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..21ab9dcd1bdc 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 @@ -18,7 +18,7 @@ package org.gradle.internal.enterprise import org.gradle.integtests.fixtures.AbstractIntegrationSpec import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager -import org.gradle.internal.enterprise.impl.DefautGradleEnterprisePluginCheckInService +import org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSpec { @@ -63,10 +63,10 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp applyPlugin() when: - succeeds "t", "-D${DefautGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE}=true" + succeeds "t", "-D${DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE}=true" then: - plugin.assertUnsupportedMessage(output, DefautGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE_MESSAGE) + plugin.assertUnsupportedMessage(output, DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_TOGGLE_MESSAGE) } def "checkin happens once for build with buildSrc"() { @@ -86,4 +86,19 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp then: plugin.serviceCreatedOnce(output) } + + def "shows warning message when unsupported Gradle Enterprise plugin version is used with configuration caching enabled"() { + given: + plugin.runtimeVersion = '3.11.4' + applyPlugin() + + when: + succeeds("t", "--configuration-cache") + + then: + plugin.notApplied(output) + + and: + plugin.assertUnsupportedMessage(output, DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_CONFIGURATION_CACHING_MESSAGE) + } } 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..253a0c9e2639 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 @@ -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..92c28e37d888 --- /dev/null +++ b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java @@ -0,0 +1,89 @@ +/* + * 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"; + + public static final VersionNumber MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING = VersionNumber.version(3, 12); + public static final String UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_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 or later of the Gradle Enterprise plugin to restore functionality.", MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING); + + @Override + public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadata pluginMetadata, GradleEnterprisePluginServiceFactory serviceFactory) { + if (Boolean.getBoolean(UNSUPPORTED_TOGGLE)) { + manager.unsupported(); + return checkInResult(UNSUPPORTED_TOGGLE_MESSAGE, () -> {throw new IllegalStateException();}); + } else { + if (isConfigurationCacheEnabled && isUnsupported(pluginMetadata.getVersion())) { + manager.unsupported(); + return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_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 static boolean isUnsupported(String pluginVersion) { + VersionNumber version = VersionNumber.parse(pluginVersion).getBaseVersion(); + return MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_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); From bb3b0ff5cd87a4a8df458c1c71dbd78b04ca2489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Guillot?= Date: Mon, 21 Nov 2022 10:53:13 +0100 Subject: [PATCH 002/120] Make private --- .../impl/DefaultGradleEnterprisePluginCheckInService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 92c28e37d888..e28dec9d76ce 100644 --- 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 @@ -47,7 +47,7 @@ public DefaultGradleEnterprisePluginCheckInService( 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"; - public static final VersionNumber MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING = VersionNumber.version(3, 12); + private static final VersionNumber MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING = VersionNumber.version(3, 12); public static final String UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_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 or later of the Gradle Enterprise plugin to restore functionality.", MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING); From 5c2ce19d4fef7552192357bbbecebeebeb928411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Guillot?= Date: Mon, 21 Nov 2022 10:55:52 +0100 Subject: [PATCH 003/120] Reduce cyclomatic complexity --- ...faultGradleEnterprisePluginCheckInService.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) 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 index e28dec9d76ce..0bc451e3b170 100644 --- 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 @@ -56,15 +56,14 @@ public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadat if (Boolean.getBoolean(UNSUPPORTED_TOGGLE)) { manager.unsupported(); return checkInResult(UNSUPPORTED_TOGGLE_MESSAGE, () -> {throw new IllegalStateException();}); - } else { - if (isConfigurationCacheEnabled && isUnsupported(pluginMetadata.getVersion())) { - manager.unsupported(); - return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_CONFIGURATION_CACHING_MESSAGE, () -> {throw new IllegalStateException();}); - } - GradleEnterprisePluginServiceRef ref = adapter.register(serviceFactory); - manager.registerAdapter(adapter); - return checkInResult(null, () -> ref); } + if (isConfigurationCacheEnabled && isUnsupported(pluginMetadata.getVersion())) { + manager.unsupported(); + return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_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) { From d9f8b2133807bfebb50188c498ebd23489a97edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Guillot?= Date: Mon, 21 Nov 2022 10:59:37 +0100 Subject: [PATCH 004/120] Rename constant and add comment From Gradle's perspective, there is no need to mention 'Gradle 8', as this is effectively always true moving forward --- ...radleEnterprisePluginCheckInIntegrationTest.groovy | 2 +- .../DefaultGradleEnterprisePluginCheckInService.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) 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 21ab9dcd1bdc..62da1d2dc01c 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 @@ -99,6 +99,6 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp plugin.notApplied(output) and: - plugin.assertUnsupportedMessage(output, DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_CONFIGURATION_CACHING_MESSAGE) + plugin.assertUnsupportedMessage(output, DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE) } } 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 index 0bc451e3b170..f851a9aa64a5 100644 --- 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 @@ -47,9 +47,10 @@ public DefaultGradleEnterprisePluginCheckInService( 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"; - private static final VersionNumber MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING = VersionNumber.version(3, 12); - public static final String UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_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 or later of the Gradle Enterprise plugin to restore functionality.", MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING); + // For Gradle versions 8+, configuration caching builds are not compatible with Gradle Enterprise plugin < 3.12 + private 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 or later of the Gradle Enterprise plugin to restore functionality.", MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING); @Override public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadata pluginMetadata, GradleEnterprisePluginServiceFactory serviceFactory) { @@ -59,7 +60,7 @@ public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadat } if (isConfigurationCacheEnabled && isUnsupported(pluginMetadata.getVersion())) { manager.unsupported(); - return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_GRADLE_8_AND_CONFIGURATION_CACHING_MESSAGE, () -> {throw new IllegalStateException();}); + return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE, () -> {throw new IllegalStateException();}); } GradleEnterprisePluginServiceRef ref = adapter.register(serviceFactory); manager.registerAdapter(adapter); @@ -82,7 +83,7 @@ public GradleEnterprisePluginServiceRef getPluginServiceRef() { private static boolean isUnsupported(String pluginVersion) { VersionNumber version = VersionNumber.parse(pluginVersion).getBaseVersion(); - return MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_GRADLE_8_AND_CONFIGURATION_CACHING.compareTo(version) < 0; + return MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.compareTo(version) < 0; } } From a0fc3f598f71189123786ff9c561395a4bb6b159 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 21 Nov 2022 16:52:57 -0500 Subject: [PATCH 005/120] Fix concurrency issue with ZipFileTree class When multiple threads are using the getFile method, it is possible for thread 2 to begin reading the file before thread 1 has finished expanding it from the zip. This adds test to reproduce to issue reliably (I think), and fixes the issue by synchronizing the expansion of zip files using this class's inner DetailsImpl. --- .../internal/file/archive/ZipFileTree.java | 17 +++- .../file/archive/ZipFileTreeTest.java | 86 ++++++++++++++++++- 2 files changed, 96 insertions(+), 7 deletions(-) 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..6fc54cb1a696 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 @@ -131,12 +131,17 @@ private File getExpandedDir() { } private static class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails { + // See https://github.com/gradle/gradle/issues/22685 - tasks in different projects + // might end up accessing and expanding the same zip file concurrently, so we need + // to synchronize using the same lock object for all zip file instances + private static final Object[] expandLock = new Object[0]; + private final File originalFile; private final File expandedDir; private final ZipArchiveEntry entry; private final ZipFile zip; private final AtomicBoolean stopFlag; - private File file; + private volatile File file; public DetailsImpl(File originalFile, File expandedDir, ZipArchiveEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod) { super(chmod); @@ -160,9 +165,13 @@ public void stopVisiting() { @Override public File getFile() { if (file == null) { - file = new File(expandedDir, safeEntryName()); - if (!file.exists()) { - copyTo(file); + synchronized (expandLock) { + if (file == null) { + file = new File(expandedDir, safeEntryName()); + if (!file.exists()) { + copyTo(file); + } + } } } return file; 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..a58dfad13cf5 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 @@ -15,26 +15,44 @@ */ package org.gradle.api.internal.file.archive; +import org.apache.commons.io.FileUtils; import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; import org.gradle.api.file.EmptyFileVisitor; +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.TestUtil; 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.HashMap; +import java.util.List; import java.util.Map; - -import static org.gradle.api.file.FileVisitorUtil.*; -import static org.gradle.api.internal.file.TestFiles.*; +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.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.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes; import static org.gradle.util.internal.WrapUtil.toList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class ZipFileTreeTest { @@ -137,4 +155,66 @@ public void doesNotOverwriteFilesOnSecondVisit() { assertVisits(tree, toList("file1.txt"), new ArrayList<>()); content.assertHasNotChangedSince(snapshot); } + + @Issue("https://github.com/gradle/gradle/issues/22685") + @Test + public void testZipVisiting() throws InterruptedException { + Long numLines = 1000000L; + int numFiles = 2; + int numThreads = 3; + + class CountingVisitor implements FileVisitor { + private 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()); + } + rootDir.zipTo(zipFile); + + // 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()); + } + + List>> callables = visitors.stream().map(v -> (Callable>) () -> { + tree.visit(v); + return v.getActualCounts(); + }).collect(Collectors.toList()); + + ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + List>> results = executorService.invokeAll(callables); + + 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); + } + }); + } } From 52172a1c7a44668902cbcef80b07a7cd47f2406e Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 21 Nov 2022 17:07:53 -0500 Subject: [PATCH 006/120] Fix checkstyle complaints --- .../org/gradle/api/internal/file/archive/ZipFileTree.java | 4 ++-- .../org/gradle/api/internal/file/archive/ZipFileTreeTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 6fc54cb1a696..3d92e36c8d02 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 @@ -134,7 +134,7 @@ private static class DetailsImpl extends AbstractFileTreeElement implements File // See https://github.com/gradle/gradle/issues/22685 - tasks in different projects // might end up accessing and expanding the same zip file concurrently, so we need // to synchronize using the same lock object for all zip file instances - private static final Object[] expandLock = new Object[0]; + private static final Object[] EXPAND_LOCK = new Object[0]; private final File originalFile; private final File expandedDir; @@ -165,7 +165,7 @@ public void stopVisiting() { @Override public File getFile() { if (file == null) { - synchronized (expandLock) { + synchronized (EXPAND_LOCK) { if (file == null) { file = new File(expandedDir, safeEntryName()); if (!file.exists()) { 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 a58dfad13cf5..29e5aa49569d 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 @@ -164,7 +164,7 @@ public void testZipVisiting() throws InterruptedException { int numThreads = 3; class CountingVisitor implements FileVisitor { - private List actualCounts = new ArrayList<>(); + private final List actualCounts = new ArrayList<>(); public List getActualCounts() { return actualCounts; From 340df850ccbe1e0d4e4fc13f005e90477ddaa697 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Tue, 22 Nov 2022 14:40:10 -0500 Subject: [PATCH 007/120] Rework solution to concurrent unarchiving issue to use a PersistentCache This adds a new AbstractArchiveFileTreeElement type as a common base class for TarFileTree and ZipFileTree, allowing both of these types to share caching logic. Also combines and removes some duplication from the tests for these types. --- .../internal/file/DefaultFileOperations.java | 24 ++- .../file/archive/AbstractArchiveFileTree.java | 26 +++ .../AbstractArchiveFileTreeElement.java | 77 ++++++++ .../internal/file/archive/TarFileTree.java | 127 +++++++------ .../internal/file/archive/ZipFileTree.java | 46 ++--- .../WorkerSharedProjectScopeServices.java | 11 +- .../file/DefaultFileOperationsTest.groovy | 6 +- ...ovy => AbstractArchiveFileTreeSpec.groovy} | 21 ++- .../archive/AbstractArchiveFileTreeTest.java | 154 ++++++++++++++++ .../file/archive/TarFileTreeTest.java | 114 +++++------- .../file/archive/ZipFileTreeTest.java | 173 ++++-------------- .../gradle/api/internal/file/TestFiles.java | 9 +- 12 files changed, 474 insertions(+), 314 deletions(-) create mode 100644 subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeElement.java rename subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/{AbstractArchiveFileTreeTest.groovy => AbstractArchiveFileTreeSpec.groovy} (71%) create mode 100644 subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.java 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..63e4cf0a9a54 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 @@ -51,6 +51,8 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.Cast; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; @@ -87,6 +89,8 @@ public class DefaultFileOperations implements FileOperations { private final FileCollectionFactory fileCollectionFactory; private final TaskDependencyFactory taskDependencyFactory; private final ProviderFactory providers; + private final CacheFactory cacheFactory; + private final GradleUserHomeDirProvider userHomeDirProvider; public DefaultFileOperations( FileResolver fileResolver, @@ -103,7 +107,9 @@ public DefaultFileOperations( Deleter deleter, DocumentationRegistry documentationRegistry, TaskDependencyFactory taskDependencyFactory, - ProviderFactory providers + ProviderFactory providers, + CacheFactory cacheFactory, + GradleUserHomeDirProvider userHomeDirProvider ) { this.fileCollectionFactory = fileCollectionFactory; this.fileResolver = fileResolver; @@ -129,6 +135,8 @@ public DefaultFileOperations( ); this.fileSystem = fileSystem; this.deleter = deleter; + this.cacheFactory = cacheFactory; + this.userHomeDirProvider = userHomeDirProvider; } @Override @@ -178,7 +186,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, cacheFactory, userHomeDirProvider), taskDependencyFactory, patternSetFactory); } @Override @@ -193,7 +201,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, cacheFactory, userHomeDirProvider); return new FileTreeAdapter(tarTree, taskDependencyFactory, patternSetFactory); } @@ -223,10 +231,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); @@ -307,6 +311,8 @@ 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); + CacheFactory cacheFactory = services.get(CacheFactory.class); + GradleUserHomeDirProvider userHomeDirProvider = services.get(GradleUserHomeDirProvider.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, @@ -331,6 +337,8 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File deleter, documentationRegistry, taskDependencyFactory, - providers); + providers, + cacheFactory, + userHomeDirProvider); } } 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..07a9d445e1ce 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,36 @@ import org.gradle.api.internal.tasks.TaskDependencyContainer; import org.gradle.api.internal.tasks.TaskDependencyResolveContext; import org.gradle.api.provider.Provider; +import org.gradle.cache.CacheRepository; +import org.gradle.cache.FileLockManager; +import org.gradle.cache.PersistentCache; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.cache.internal.CacheScopeMapping; +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.util.GradleVersion; import java.io.File; +import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; + public abstract class AbstractArchiveFileTree implements FileSystemMirroringFileTree, TaskDependencyContainer { + private static final String EXPANSION_CACHE_KEY = "compressed-file-expansion"; + private static final String EXPANSION_CACHE_NAME = "Compressed Files Expansion Cache"; + + protected final PersistentCache expansionCache; + + protected AbstractArchiveFileTree(CacheFactory cacheFactory, GradleUserHomeDirProvider userHomeDirProvider) { + CacheScopeMapping cacheScopeMapping = new DefaultCacheScopeMapping(new GlobalCacheDir(userHomeDirProvider).getDir(), GradleVersion.current()); + CacheRepository cacheRepository = new DefaultCacheRepository(cacheScopeMapping, cacheFactory); + this.expansionCache = cacheRepository.cache(EXPANSION_CACHE_KEY) + .withDisplayName(EXPANSION_CACHE_NAME) + .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) + .open(); + } + 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..9ad81120bde9 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeElement.java @@ -0,0 +1,77 @@ +/* + * 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 com.google.common.base.Preconditions; +import org.gradle.api.internal.file.AbstractFileTreeElement; +import org.gradle.cache.PersistentCache; +import org.gradle.internal.file.Chmod; +import org.gradle.util.internal.GFileUtils; + +import java.io.File; + +/** + * 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 cache directory. + */ +public abstract class AbstractArchiveFileTreeElement extends AbstractFileTreeElement { + private final PersistentCache expansionCache; + private final File expandedDir; + private File file; + + /** + * Creates a new instance. + * + * @param expansionCache the cache to use for extracting the archive + * @param expandedDir the directory to extract the archive to (must be a subdirectory of the base cache directory) + * @param chmod the chmod instance to use + */ + protected AbstractArchiveFileTreeElement(Chmod chmod, PersistentCache expansionCache, File expandedDir) { + super(chmod); + + Preconditions.checkArgument(expandedDir.getParentFile().equals(expansionCache.getBaseDir()), "Expanded dir must be located in the given cache"); + this.expansionCache = expansionCache; + this.expandedDir = expandedDir; + } + + /** + * 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(); + + private File expandToCache(String entryName) { + return expansionCache.useCache(() -> { + File file = new File(expandedDir, entryName); + if (!file.exists()) { + GFileUtils.mkdirs(file.getParentFile()); + copyTo(file); + } + return file; + }); + } + + @Override + public File getFile() { + if (file == null) { + file = expandToCache(safeEntryName()); + } + return file; + } +} 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..0e091e0fd4d3 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 @@ -23,12 +23,14 @@ 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.PersistentCache; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.hash.HashCode; @@ -47,15 +49,21 @@ 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, + CacheFactory cacheFactory, + GradleUserHomeDirProvider userHomeDirProvider) { + super(cacheFactory, userHomeDirProvider); this.tarFileProvider = tarFileProvider; this.resource = resource; this.chmod = chmod; this.directoryFileTreeFactory = directoryFileTreeFactory; - this.tmpDir = tmpDir; this.fileHasher = fileHasher; } @@ -104,9 +112,9 @@ private void visitImpl(FileVisitor visitor, InputStream inputStream) throws IOEx TarArchiveEntry entry; while (!stopFlag.get() && (entry = (TarArchiveEntry) tar.getNextEntry()) != null) { if (entry.isDirectory()) { - visitor.visitDir(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod)); + visitor.visitDir(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod, expansionCache)); } else { - visitor.visitFile(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod)); + visitor.visitFile(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod, expansionCache)); } } } @@ -120,7 +128,7 @@ private File getExpandedDir() { File tarFile = tarFileProvider.get(); HashCode fileHash = hashFile(tarFile); String expandedDirName = tarFile.getName() + "_" + fileHash; - return new File(tmpDir, expandedDirName); + return new File(expansionCache.getBaseDir(), expandedDirName); } private HashCode hashFile(File tarFile) { @@ -135,19 +143,54 @@ private RuntimeException cannotExpand(Exception e) { throw new InvalidUserDataException(String.format("Cannot expand %s.", getDisplayName()), e); } - private static class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails { + /** + * 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. + * + * This is a simplified version of ArchiveStreamFactory.detect(InputStream), + * and extended to not throw an exception for empty TAR files (i.e. ones with no entries in them). + */ + private void checkFormat(InputStream inputStream) throws IOException { + if (!inputStream.markSupported()) { + throw new IOException("TAR input stream does not support mark/reset."); + } + + int tarHeaderSize = 512; // ArchiveStreamFactory.TAR_HEADER_SIZE + inputStream.mark(tarHeaderSize); + final byte[] tarHeader = new byte[tarHeaderSize]; + int signatureLength = IOUtils.readFully(inputStream, tarHeader); + inputStream.reset(); + if (TarArchiveInputStream.matches(tarHeader, signatureLength)) { + return; + } + + if (signatureLength >= tarHeaderSize) { + try (TarArchiveInputStream tais = new TarArchiveInputStream(new ByteArrayInputStream(tarHeader))) { + if (tais.getNextTarEntry() == null) { + // empty TAR + return; + } + if (tais.getNextTarEntry().isCheckSumOK()) { + return; + } + } catch (Exception e) { + // can generate IllegalArgumentException as well as IOException + // not a TAR ignored + } + } + throw new IOException("Not a TAR archive"); + } + + private static class DetailsImpl extends AbstractArchiveFileTreeElement 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); + public DetailsImpl(ReadableResourceInternal resource, File expandedDir, TarArchiveEntry entry, NoCloseTarArchiveInputStream tar, AtomicBoolean stopFlag, Chmod chmod, PersistentCache expansionCache) { + super(chmod, expansionCache, expandedDir); this.resource = resource; - this.expandedDir = expandedDir; this.entry = entry; this.tar = tar; this.stopFlag = stopFlag; @@ -163,17 +206,6 @@ 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(); @@ -191,8 +223,8 @@ public long getSize() { @Override public InputStream open() { - if (read && file != null) { - return GFileUtils.openInputStream(file); + if (read && getFile() != null) { + return GFileUtils.openInputStream(getFile()); } if (read || tar.getCurrentEntry() != entry) { throw new UnsupportedOperationException(String.format("The contents of %s has already been read.", this)); @@ -210,6 +242,11 @@ public RelativePath getRelativePath() { public int getMode() { return entry.getMode() & 0777; } + + @Override + protected String safeEntryName() { + return entry.getName(); + } } private static class NoCloseTarArchiveInputStream extends TarArchiveInputStream { @@ -221,42 +258,4 @@ public NoCloseTarArchiveInputStream(InputStream is) { 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. - * - * This is a simplified version of ArchiveStreamFactory.detect(InputStream), - * and extended to not throw an exception for empty TAR files (i.e. ones with no entries in them). - */ - private void checkFormat(InputStream inputStream) throws IOException { - if (!inputStream.markSupported()) { - throw new IOException("TAR input stream does not support mark/reset."); - } - - int tarHeaderSize = 512; // ArchiveStreamFactory.TAR_HEADER_SIZE - inputStream.mark(tarHeaderSize); - final byte[] tarHeader = new byte[tarHeaderSize]; - int signatureLength = IOUtils.readFully(inputStream, tarHeader); - inputStream.reset(); - if (TarArchiveInputStream.matches(tarHeader, signatureLength)) { - return; - } - - if (signatureLength >= tarHeaderSize) { - try (TarArchiveInputStream tais = new TarArchiveInputStream(new ByteArrayInputStream(tarHeader))) { - if (tais.getNextTarEntry() == null) { - // empty TAR - return; - } - if (tais.getNextTarEntry().isCheckSumOK()) { - return; - } - } catch (Exception e) { - // can generate IllegalArgumentException as well as IOException - // not a TAR ignored - } - } - throw new IOException("Not a TAR archive"); - } } 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 3d92e36c8d02..c05bc957be19 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 @@ -23,10 +23,12 @@ 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.PersistentCache; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.nativeintegration.filesystem.FileSystem; @@ -45,20 +47,20 @@ 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, + CacheFactory cacheFactory, + GradleUserHomeDirProvider userHomeDirProvider ) { + super(cacheFactory, userHomeDirProvider); this.fileProvider = zipFile; - this.tmpDir = tmpDir; this.chmod = chmod; this.directoryFileTreeFactory = directoryFileTreeFactory; this.fileHasher = fileHasher; @@ -97,7 +99,7 @@ public void visit(FileVisitor visitor) { Iterator sortedEntries = entriesSortedByName(zip); while (!stopFlag.get() && sortedEntries.hasNext()) { ZipArchiveEntry entry = sortedEntries.next(); - DetailsImpl details = new DetailsImpl(zipFile, expandedDir, entry, zip, stopFlag, chmod); + DetailsImpl details = new DetailsImpl(zipFile, expandedDir, entry, zip, stopFlag, chmod, expansionCache); if (entry.isDirectory()) { visitor.visitDir(details); } else { @@ -127,26 +129,18 @@ public Provider getBackingFileProvider() { private File getExpandedDir() { File zipFile = fileProvider.get(); String expandedDirName = zipFile.getName() + "_" + fileHasher.hash(zipFile); - return new File(tmpDir, expandedDirName); + return new File(expansionCache.getBaseDir(), expandedDirName); } - private static class DetailsImpl extends AbstractFileTreeElement implements FileVisitDetails { - // See https://github.com/gradle/gradle/issues/22685 - tasks in different projects - // might end up accessing and expanding the same zip file concurrently, so we need - // to synchronize using the same lock object for all zip file instances - private static final Object[] EXPAND_LOCK = new Object[0]; - + private static class DetailsImpl extends AbstractArchiveFileTreeElement implements FileVisitDetails { private final File originalFile; - private final File expandedDir; private final ZipArchiveEntry entry; private final ZipFile zip; private final AtomicBoolean stopFlag; - private volatile File file; - public DetailsImpl(File originalFile, File expandedDir, ZipArchiveEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod) { - super(chmod); + public DetailsImpl(File originalFile, File expandedDir, ZipArchiveEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod, PersistentCache expansionCache) { + super(chmod, expansionCache, expandedDir); this.originalFile = originalFile; - this.expandedDir = expandedDir; this.entry = entry; this.zip = zip; this.stopFlag = stopFlag; @@ -163,21 +157,7 @@ public void stopVisiting() { } @Override - public File getFile() { - if (file == null) { - synchronized (EXPAND_LOCK) { - 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()); } 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..e10b4686206f 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 @@ -44,6 +44,8 @@ import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.tasks.util.PatternSet; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -94,7 +96,9 @@ protected DefaultFileOperations createFileOperations( Deleter deleter, DocumentationRegistry documentationRegistry, ProviderFactory providers, - TaskDependencyFactory taskDependencyFactory + TaskDependencyFactory taskDependencyFactory, + CacheFactory cacheFactory, + GradleUserHomeDirProvider userHomeDirProvider ) { return new DefaultFileOperations( fileResolver, @@ -110,7 +114,10 @@ protected DefaultFileOperations createFileOperations( patternSetFactory, deleter, documentationRegistry, - taskDependencyFactory, providers); + taskDependencyFactory, + providers, + cacheFactory, + userHomeDirProvider); } protected FileSystemOperations createFileSystemOperations(Instantiator instantiator, FileOperations fileOperations) { 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..ed2ac6e2cd1d 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 @@ -26,6 +26,7 @@ import org.gradle.api.internal.file.collections.FileTreeAdapter import org.gradle.api.internal.file.copy.DefaultCopySpec import org.gradle.api.internal.file.temp.TemporaryFileProvider import org.gradle.api.internal.resources.DefaultResourceHandler +import org.gradle.initialization.GradleUserHomeDirProvider import org.gradle.internal.hash.FileHasher import org.gradle.internal.hash.StreamHasher import org.gradle.internal.reflect.Instantiator @@ -69,7 +70,9 @@ class DefaultFileOperationsTest extends Specification { TestFiles.deleter(), TestFiles.documentationRegistry(), TestFiles.taskDependencyFactory(), - TestUtil.providerFactory() + TestUtil.providerFactory(), + TestFiles.cacheFactory(), + (GradleUserHomeDirProvider)(() -> temporaryFileProvider.newTemporaryDirectory("user-home")) ) } @@ -249,4 +252,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 71% 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..882010668edf 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 @@ -18,24 +18,32 @@ package org.gradle.api.internal.file.archive import org.gradle.api.file.FileVisitor import org.gradle.api.internal.file.FileTreeInternal +import org.gradle.api.internal.file.TestFiles 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.CacheFactory +import org.gradle.initialization.GradleUserHomeDirProvider 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()) + public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()) def "visits structure when backing file is known"() { def owner = Stub(FileTreeInternal) def visitor = Mock(MinimalFileTree.MinimalFileTreeStructureVisitor) def backingFile = tmpDir.createFile("thing.bin") - def fileTree = new TestArchiveFileTree(backingFile: backingFile) + def fileTree = new TestArchiveFileTree( + TestFiles.cacheFactory(), + () -> tmpDir.createDir("user-home"), + backingFile) when: fileTree.visitStructure(visitor, owner) @@ -49,6 +57,11 @@ class AbstractArchiveFileTreeTest extends Specification { File backingFile final String displayName = "" + TestArchiveFileTree(CacheFactory cacheFactory, GradleUserHomeDirProvider userHomeDirProvider, File backingFile) { + super(cacheFactory, userHomeDirProvider) + 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..ed1ddc5f5f06 --- /dev/null +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeTest.java @@ -0,0 +1,154 @@ +/* + * 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 userHome = tempDirProvider.getTestDirectory().file("user-home"); + 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; + + 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()); + } + + List>> callables = visitors.stream().map(v -> (Callable>) () -> { + getTree().visit(v); + return v.getActualCounts(); + }).collect(Collectors.toList()); + + ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + List>> results = executorService.invokeAll(callables); + + 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..6e50fd9a9af4 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.test.fixtures.file.TestFile; -import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider; +import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; 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,57 @@ 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(), + TestFiles.cacheFactory(), + () -> userHome); 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(), new TestInMemoryCacheFactory(), () -> userHome); assertVisits(tree, toList("subdir/file1.txt", "subdir2/file2.txt"), toList("subdir", "subdir2")); assertSetContainsForAllTypes(tree, toList("subdir/file1.txt", "subdir2/file2.txt")); @@ -91,69 +96,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(), new TestInMemoryCacheFactory(), () -> userHome); 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 +159,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 29e5aa49569d..968ab09c1469 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 @@ -15,115 +15,93 @@ */ package org.gradle.api.internal.file.archive; -import org.apache.commons.io.FileUtils; import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; import org.gradle.api.file.EmptyFileVisitor; -import org.gradle.api.file.FileVisitDetails; -import org.gradle.api.file.FileVisitor; +import org.gradle.api.internal.file.TestFiles; 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 spock.lang.Issue; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -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.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.gradle.api.tasks.AntBuilderAwareUtil.assertSetContainsForAllTypes; -import static org.gradle.util.internal.WrapUtil.toList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; 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(), + TestFiles.cacheFactory(), + () -> userHome); + + @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); @@ -134,7 +112,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); @@ -142,79 +120,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); - } - - @Issue("https://github.com/gradle/gradle/issues/22685") - @Test - public void testZipVisiting() throws InterruptedException { - Long numLines = 1000000L; - int numFiles = 2; - int numThreads = 3; - - 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()); - } - rootDir.zipTo(zipFile); - - // 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()); - } - - List>> callables = visitors.stream().map(v -> (Callable>) () -> { - tree.visit(v); - return v.getActualCounts(); - }).collect(Collectors.toList()); - - ExecutorService executorService = Executors.newFixedThreadPool(numThreads); - List>> results = executorService.invokeAll(callables); - - 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); - } - }); - } } 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..7334affd7ccd 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 @@ -52,6 +52,7 @@ import org.gradle.process.internal.ExecHandleFactory; import org.gradle.process.internal.JavaExecHandleFactory; import org.gradle.testfixtures.internal.NativeServicesTestFixture; +import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; import org.gradle.util.TestUtil; import javax.annotation.Nullable; @@ -172,7 +173,9 @@ public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFi deleter(), documentationRegistry(), taskDependencyFactory(), - providerFactory()); + providerFactory(), + cacheFactory(), + () -> temporaryFileProvider.newTemporaryDirectory("user-home")); } public static ApiTextResourceAdapter.Factory textResourceAdapterFactory(@Nullable TemporaryFileProvider temporaryFileProvider) { @@ -277,4 +280,8 @@ public static String systemSpecificAbsolutePath(String path) { public static TemporaryFileProvider tmpDirTemporaryFileProvider(File baseDir) { return new DefaultTemporaryFileProvider(() -> baseDir); } + + public static TestInMemoryCacheFactory cacheFactory() { + return new TestInMemoryCacheFactory(); + } } From b22d85e7bf63435bb3275f2b7c40ea1c09a369b3 Mon Sep 17 00:00:00 2001 From: Guy Brand Date: Thu, 24 Nov 2022 17:06:13 +0100 Subject: [PATCH 008/120] Fix tests --- ...erprisePluginCheckInIntegrationTest.groovy | 23 ++++++++++++++++--- ...tGradleEnterprisePluginCheckInService.java | 8 ++++--- 2 files changed, 25 insertions(+), 6 deletions(-) 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 62da1d2dc01c..3efa0f5e3f1b 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,12 @@ 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.DefaultGradleEnterprisePluginCheckInService +import spock.lang.IgnoreIf + +import static org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService.MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSpec { @@ -33,7 +37,6 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp """ } - void applyPlugin() { settingsFile << plugin.plugins() } @@ -87,18 +90,32 @@ class GradleEnterprisePluginCheckInIntegrationTest extends AbstractIntegrationSp 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 = '3.11.4' + plugin.runtimeVersion = pluginVersion applyPlugin() + settingsFile << """ + println "present: " + services.get($GradleEnterprisePluginManager.name).present + """ when: succeeds("t", "--configuration-cache") then: - plugin.notApplied(output) + output.contains("present: ${applied}") and: plugin.assertUnsupportedMessage(output, DefaultGradleEnterprisePluginCheckInService.UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE) + + 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/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/impl/DefaultGradleEnterprisePluginCheckInService.java index f851a9aa64a5..812a148c7606 100644 --- 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 @@ -48,9 +48,11 @@ public DefaultGradleEnterprisePluginCheckInService( 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 - private static final VersionNumber MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING = VersionNumber.version(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 or later of the Gradle Enterprise plugin to restore functionality.", MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING); + "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) { @@ -83,7 +85,7 @@ public GradleEnterprisePluginServiceRef getPluginServiceRef() { private static boolean isUnsupported(String pluginVersion) { VersionNumber version = VersionNumber.parse(pluginVersion).getBaseVersion(); - return MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.compareTo(version) < 0; + return MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.compareTo(version) > 0; } } From 1b4fdd5f2aa08e84f507f26d4b20e38a5f6e4797 Mon Sep 17 00:00:00 2001 From: Guy Brand Date: Thu, 24 Nov 2022 20:40:02 +0100 Subject: [PATCH 009/120] Fix checkstyle --- .../DefaultGradleEnterprisePluginCheckInService.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 index 812a148c7606..149de0cd4bfc 100644 --- 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 @@ -50,7 +50,7 @@ public DefaultGradleEnterprisePluginCheckInService( // 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.", + "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()); @@ -58,11 +58,15 @@ public DefaultGradleEnterprisePluginCheckInService( public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadata pluginMetadata, GradleEnterprisePluginServiceFactory serviceFactory) { if (Boolean.getBoolean(UNSUPPORTED_TOGGLE)) { manager.unsupported(); - return checkInResult(UNSUPPORTED_TOGGLE_MESSAGE, () -> {throw new IllegalStateException();}); + return checkInResult(UNSUPPORTED_TOGGLE_MESSAGE, () -> { + throw new IllegalStateException(); + }); } if (isConfigurationCacheEnabled && isUnsupported(pluginMetadata.getVersion())) { manager.unsupported(); - return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE, () -> {throw new IllegalStateException();}); + return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE, () -> { + throw new IllegalStateException(); + }); } GradleEnterprisePluginServiceRef ref = adapter.register(serviceFactory); manager.registerAdapter(adapter); From 0bcb7ddc86788fd6d0e45f6a99bc2c19161d893b Mon Sep 17 00:00:00 2001 From: Guy Brand Date: Fri, 25 Nov 2022 10:30:49 +0100 Subject: [PATCH 010/120] Polish methods --- .../impl/DefaultGradleEnterprisePluginCheckInService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 149de0cd4bfc..484bf43c2ac1 100644 --- 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 @@ -62,7 +62,7 @@ public GradleEnterprisePluginCheckInResult checkIn(GradleEnterprisePluginMetadat throw new IllegalStateException(); }); } - if (isConfigurationCacheEnabled && isUnsupported(pluginMetadata.getVersion())) { + if (isUnsupportedWithConfigurationCaching(pluginMetadata)) { manager.unsupported(); return checkInResult(UNSUPPORTED_PLUGIN_DUE_TO_CONFIGURATION_CACHING_MESSAGE, () -> { throw new IllegalStateException(); @@ -87,9 +87,9 @@ public GradleEnterprisePluginServiceRef getPluginServiceRef() { }; } - private static boolean isUnsupported(String pluginVersion) { - VersionNumber version = VersionNumber.parse(pluginVersion).getBaseVersion(); - return MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.compareTo(version) > 0; + private boolean isUnsupportedWithConfigurationCaching(GradleEnterprisePluginMetadata pluginMetadata) { + VersionNumber version = VersionNumber.parse(pluginMetadata.getVersion()).getBaseVersion(); + return isConfigurationCacheEnabled && MINIMUM_SUPPORTED_PLUGIN_VERSION_FOR_CONFIGURATION_CACHING.compareTo(version) > 0; } } From 1d20167fe02ce76330c8acc1a3b9492d2dff4eb5 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 30 Nov 2022 11:39:36 -0500 Subject: [PATCH 011/120] Move expansion cache to FileTree from Visitor implementation This will lock once per archive being visited, instead of reobtaining the lock for each entry in the archive. This change also moves additional common functionality between the DetailsImpl element classes of the Zip and Tar file trees into the abstract base class. --- .../AbstractArchiveFileTreeElement.java | 68 +++++++++---- .../internal/file/archive/TarFileTree.java | 97 +++++++------------ .../internal/file/archive/ZipFileTree.java | 83 ++++++---------- 3 files changed, 114 insertions(+), 134 deletions(-) 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 index 9ad81120bde9..1691f562ac25 100644 --- 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 @@ -16,62 +16,88 @@ package org.gradle.api.internal.file.archive; -import com.google.common.base.Preconditions; +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.cache.PersistentCache; 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 cache directory. + * This implementation extracts the files from the archive to the supplied expansion directory. */ -public abstract class AbstractArchiveFileTreeElement extends AbstractFileTreeElement { - private final PersistentCache expansionCache; +public abstract class AbstractArchiveFileTreeElement extends AbstractFileTreeElement implements FileVisitDetails { private final File expandedDir; private File file; + private final AtomicBoolean stopFlag; /** * Creates a new instance. * - * @param expansionCache the cache to use for extracting the archive - * @param expandedDir the directory to extract the archive to (must be a subdirectory of the base cache directory) * @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, PersistentCache expansionCache, File expandedDir) { + protected AbstractArchiveFileTreeElement(Chmod chmod, File expandedDir, AtomicBoolean stopFlag) { super(chmod); - - Preconditions.checkArgument(expandedDir.getParentFile().equals(expansionCache.getBaseDir()), "Expanded dir must be located in the given cache"); - this.expansionCache = expansionCache; 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(); - private File expandToCache(String entryName) { - return expansionCache.useCache(() -> { - File file = new File(expandedDir, entryName); + @Override + public File getFile() { + if (file == null) { + file = new File(expandedDir, safeEntryName()); if (!file.exists()) { GFileUtils.mkdirs(file.getParentFile()); copyTo(file); } - return file; - }); + } + return file; } @Override - public File getFile() { - if (file == null) { - file = expandToCache(safeEntryName()); - } - return file; + 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 0e091e0fd4d3..ca5f7e25088f 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,12 @@ 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.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.PersistentCache; import org.gradle.cache.internal.CacheFactory; import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.file.Chmod; @@ -79,42 +76,44 @@ public DirectoryFileTree getMirror() { @Override public void visit(FileVisitor visitor) { - InputStream inputStream; - try { - inputStream = new BufferedInputStream(resource.get().read()); - } catch (ResourceException e) { - throw cannotExpand(e); - } + expansionCache.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; while (!stopFlag.get() && (entry = (TarArchiveEntry) tar.getNextEntry()) != null) { if (entry.isDirectory()) { - visitor.visitDir(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod, expansionCache)); + visitor.visitDir(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod)); } else { - visitor.visitFile(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod, expansionCache)); + visitor.visitFile(new DetailsImpl(resource, expandedDir, entry, tar, stopFlag, chmod)); } } } @@ -181,19 +180,17 @@ private void checkFormat(InputStream inputStream) throws IOException { throw new IOException("Not a TAR archive"); } - private static class DetailsImpl extends AbstractArchiveFileTreeElement implements FileVisitDetails { + private static final class DetailsImpl extends AbstractArchiveFileTreeElement { private final TarArchiveEntry entry; private final NoCloseTarArchiveInputStream tar; - private final AtomicBoolean stopFlag; private final ReadableResourceInternal resource; private boolean read; - public DetailsImpl(ReadableResourceInternal resource, File expandedDir, TarArchiveEntry entry, NoCloseTarArchiveInputStream tar, AtomicBoolean stopFlag, Chmod chmod, PersistentCache expansionCache) { - super(chmod, expansionCache, expandedDir); + 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; - this.stopFlag = stopFlag; } @Override @@ -201,26 +198,6 @@ public String getDisplayName() { return String.format("tar entry %s!%s", resource.getDisplayName(), entry.getName()); } - @Override - public void stopVisiting() { - stopFlag.set(true); - } - - @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 && getFile() != null) { @@ -233,11 +210,6 @@ public InputStream open() { return tar; } - @Override - public RelativePath getRelativePath() { - return new RelativePath(!entry.isDirectory(), entry.getName().split("/")); - } - @Override public int getMode() { return entry.getMode() & 0777; @@ -247,15 +219,20 @@ public int getMode() { protected String safeEntryName() { return entry.getName(); } - } - private static class NoCloseTarArchiveInputStream extends TarArchiveInputStream { - public NoCloseTarArchiveInputStream(InputStream is) { - super(is); + @Override + protected TarArchiveEntry getArchiveEntry() { + return entry; } - @Override - public void close() throws IOException { + 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 c05bc957be19..5e4e8309bf63 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,10 @@ 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.collections.DirectoryFileTree; import org.gradle.api.internal.file.collections.DirectoryFileTreeFactory; import org.gradle.api.provider.Provider; -import org.gradle.cache.PersistentCache; import org.gradle.cache.internal.CacheFactory; import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.file.Chmod; @@ -83,32 +80,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())); - } + expansionCache.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, expansionCache); - 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) { @@ -132,18 +131,16 @@ private File getExpandedDir() { return new File(expansionCache.getBaseDir(), expandedDirName); } - private static class DetailsImpl extends AbstractArchiveFileTreeElement implements FileVisitDetails { + private static final class DetailsImpl extends AbstractArchiveFileTreeElement { private final File originalFile; private final ZipArchiveEntry entry; private final ZipFile zip; - private final AtomicBoolean stopFlag; - public DetailsImpl(File originalFile, File expandedDir, ZipArchiveEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod, PersistentCache expansionCache) { - super(chmod, expansionCache, expandedDir); + public DetailsImpl(File originalFile, File expandedDir, ZipArchiveEntry entry, ZipFile zip, AtomicBoolean stopFlag, Chmod chmod) { + super(chmod, expandedDir, stopFlag); this.originalFile = originalFile; this.entry = entry; this.zip = zip; - this.stopFlag = stopFlag; } @Override @@ -151,29 +148,14 @@ public String getDisplayName() { return format("zip entry %s!%s", originalFile, entry.getName()); } - @Override - public void stopVisiting() { - stopFlag.set(true); - } - @Override 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 @@ -185,11 +167,6 @@ public InputStream open() { } } - @Override - public RelativePath getRelativePath() { - return new RelativePath(!entry.isDirectory(), safeEntryName().split("/")); - } - @Override public int getMode() { int unixMode = entry.getUnixMode() & 0777; From 27c47bca3bcb11204802a725cea53118c3796b62 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 30 Nov 2022 11:41:09 -0500 Subject: [PATCH 012/120] Simplify conditional tests per IDE nullability analysis --- .../gradle/api/internal/file/archive/TarFileTree.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 ca5f7e25088f..19d7f66c62cb 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 @@ -200,14 +200,15 @@ public String getDisplayName() { @Override public InputStream open() { - if (read && getFile() != null) { + if (read) { + getFile(); return GFileUtils.openInputStream(getFile()); - } - if (read || tar.getCurrentEntry() != entry) { + } else if (tar.getCurrentEntry() != entry) { throw new UnsupportedOperationException(String.format("The contents of %s has already been read.", this)); + } else { + read = true; + return tar; } - read = true; - return tar; } @Override From ca72b4757e4beb24be87fec042e298791ddbe00e Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 30 Nov 2022 16:24:14 -0500 Subject: [PATCH 013/120] Switch to injecting a scoped cache into archive tree types, vs. creating one within them This uses a BuildScopedCache to begin. It also adds test utilities for creating a scoped cache using the TestInMemoryCacheFactory to back it. --- .../internal/file/DefaultFileOperations.java | 23 ++-- .../file/archive/AbstractArchiveFileTree.java | 15 +-- .../internal/file/archive/TarFileTree.java | 8 +- .../internal/file/archive/ZipFileTree.java | 8 +- .../WorkerSharedProjectScopeServices.java | 9 +- .../file/archive/TarFileTreeTest.java | 8 +- .../file/archive/ZipFileTreeTest.java | 3 +- .../gradle/api/internal/file/TestFiles.java | 109 +++++++++++++++++- 8 files changed, 130 insertions(+), 53 deletions(-) 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 63e4cf0a9a54..37f67a054e8f 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 @@ -51,8 +51,8 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; -import org.gradle.cache.internal.CacheFactory; -import org.gradle.initialization.GradleUserHomeDirProvider; +import org.gradle.cache.scopes.BuildScopedCache; +import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Cast; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; @@ -89,8 +89,7 @@ public class DefaultFileOperations implements FileOperations { private final FileCollectionFactory fileCollectionFactory; private final TaskDependencyFactory taskDependencyFactory; private final ProviderFactory providers; - private final CacheFactory cacheFactory; - private final GradleUserHomeDirProvider userHomeDirProvider; + private final ScopedCache cacheBuilder; public DefaultFileOperations( FileResolver fileResolver, @@ -108,8 +107,7 @@ public DefaultFileOperations( DocumentationRegistry documentationRegistry, TaskDependencyFactory taskDependencyFactory, ProviderFactory providers, - CacheFactory cacheFactory, - GradleUserHomeDirProvider userHomeDirProvider + ScopedCache cacheBuilder ) { this.fileCollectionFactory = fileCollectionFactory; this.fileResolver = fileResolver; @@ -135,8 +133,7 @@ public DefaultFileOperations( ); this.fileSystem = fileSystem; this.deleter = deleter; - this.cacheFactory = cacheFactory; - this.userHomeDirProvider = userHomeDirProvider; + this.cacheBuilder = cacheBuilder; } @Override @@ -186,7 +183,7 @@ public ConfigurableFileTree fileTree(Map args) { @Override public FileTreeInternal zipTree(Object zipPath) { Provider fileProvider = asFileProvider(zipPath); - return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, cacheFactory, userHomeDirProvider), taskDependencyFactory, patternSetFactory); + return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, cacheBuilder), taskDependencyFactory, patternSetFactory); } @Override @@ -201,7 +198,7 @@ public FileTreeInternal tarTree(Object tarPath) { } }); - TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, cacheFactory, userHomeDirProvider); + TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, cacheBuilder); return new FileTreeAdapter(tarTree, taskDependencyFactory, patternSetFactory); } @@ -311,8 +308,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); - CacheFactory cacheFactory = services.get(CacheFactory.class); - GradleUserHomeDirProvider userHomeDirProvider = services.get(GradleUserHomeDirProvider.class); + BuildScopedCache decompressionCache = services.get(BuildScopedCache.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, @@ -338,7 +334,6 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File documentationRegistry, taskDependencyFactory, providers, - cacheFactory, - userHomeDirProvider); + decompressionCache); } } 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 07a9d445e1ce..022a98f85b1b 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,16 +21,9 @@ import org.gradle.api.internal.tasks.TaskDependencyContainer; import org.gradle.api.internal.tasks.TaskDependencyResolveContext; import org.gradle.api.provider.Provider; -import org.gradle.cache.CacheRepository; import org.gradle.cache.FileLockManager; import org.gradle.cache.PersistentCache; -import org.gradle.cache.internal.CacheFactory; -import org.gradle.cache.internal.CacheScopeMapping; -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.util.GradleVersion; +import org.gradle.cache.scopes.ScopedCache; import java.io.File; @@ -42,10 +35,8 @@ public abstract class AbstractArchiveFileTree implements FileSystemMirroringFile protected final PersistentCache expansionCache; - protected AbstractArchiveFileTree(CacheFactory cacheFactory, GradleUserHomeDirProvider userHomeDirProvider) { - CacheScopeMapping cacheScopeMapping = new DefaultCacheScopeMapping(new GlobalCacheDir(userHomeDirProvider).getDir(), GradleVersion.current()); - CacheRepository cacheRepository = new DefaultCacheRepository(cacheScopeMapping, cacheFactory); - this.expansionCache = cacheRepository.cache(EXPANSION_CACHE_KEY) + protected AbstractArchiveFileTree(ScopedCache cacheBuilder) { + this.expansionCache = cacheBuilder.cache(EXPANSION_CACHE_KEY) .withDisplayName(EXPANSION_CACHE_NAME) .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) .open(); 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 19d7f66c62cb..bf12c6839d61 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 @@ -26,8 +26,7 @@ import org.gradle.api.provider.Provider; import org.gradle.api.resources.ResourceException; import org.gradle.api.resources.internal.ReadableResourceInternal; -import org.gradle.cache.internal.CacheFactory; -import org.gradle.initialization.GradleUserHomeDirProvider; +import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.hash.HashCode; @@ -54,9 +53,8 @@ public TarFileTree( Chmod chmod, DirectoryFileTreeFactory directoryFileTreeFactory, FileHasher fileHasher, - CacheFactory cacheFactory, - GradleUserHomeDirProvider userHomeDirProvider) { - super(cacheFactory, userHomeDirProvider); + ScopedCache cacheBuilder) { + super(cacheBuilder); this.tarFileProvider = tarFileProvider; this.resource = resource; this.chmod = chmod; 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 5e4e8309bf63..7371a2ce2f49 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 @@ -24,8 +24,7 @@ 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.CacheFactory; -import org.gradle.initialization.GradleUserHomeDirProvider; +import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.nativeintegration.filesystem.FileSystem; @@ -53,10 +52,9 @@ public ZipFileTree( Chmod chmod, DirectoryFileTreeFactory directoryFileTreeFactory, FileHasher fileHasher, - CacheFactory cacheFactory, - GradleUserHomeDirProvider userHomeDirProvider + ScopedCache cacheBuilder ) { - super(cacheFactory, userHomeDirProvider); + super(cacheBuilder); this.fileProvider = zipFile; this.chmod = chmod; this.directoryFileTreeFactory = directoryFileTreeFactory; 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 e10b4686206f..22a6992f4500 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 @@ -44,8 +44,7 @@ import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.tasks.util.PatternSet; -import org.gradle.cache.internal.CacheFactory; -import org.gradle.initialization.GradleUserHomeDirProvider; +import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -97,8 +96,7 @@ protected DefaultFileOperations createFileOperations( DocumentationRegistry documentationRegistry, ProviderFactory providers, TaskDependencyFactory taskDependencyFactory, - CacheFactory cacheFactory, - GradleUserHomeDirProvider userHomeDirProvider + ScopedCache cacheBuilder ) { return new DefaultFileOperations( fileResolver, @@ -116,8 +114,7 @@ protected DefaultFileOperations createFileOperations( documentationRegistry, taskDependencyFactory, providers, - cacheFactory, - userHomeDirProvider); + cacheBuilder); } protected FileSystemOperations createFileSystemOperations(Instantiator instantiator, FileOperations fileOperations) { 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 6e50fd9a9af4..fa9d934ce9de 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 @@ -24,7 +24,6 @@ import org.gradle.api.resources.MissingResourceException; import org.gradle.api.resources.internal.LocalResourceAdapter; import org.gradle.test.fixtures.file.TestFile; -import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; import org.gradle.util.TestUtil; import org.junit.Test; @@ -52,8 +51,7 @@ public class TarFileTreeTest extends AbstractArchiveFileTreeTest { fileSystem(), directoryFileTreeFactory(), fileHasher(), - TestFiles.cacheFactory(), - () -> userHome); + TestFiles.scopedCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); private static Provider asProvider(T object) { return TestUtil.providerFactory().provider(() -> object); @@ -88,7 +86,7 @@ public void readsGzippedTarFile() { rootDir.tgzTo(tgz); MaybeCompressedFileResource resource = new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tgz))); - TarFileTree tree = new TarFileTree(asProvider(tgz), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), new TestInMemoryCacheFactory(), () -> userHome); + TarFileTree tree = new TarFileTree(asProvider(tgz), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), TestFiles.scopedCache(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")); @@ -103,7 +101,7 @@ public void readsBzippedTarFile() { rootDir.tbzTo(tbz2); MaybeCompressedFileResource resource = new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tbz2))); - TarFileTree tree = new TarFileTree(asProvider(tbz2), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), new TestInMemoryCacheFactory(), () -> userHome); + TarFileTree tree = new TarFileTree(asProvider(tbz2), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), TestFiles.scopedCache(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")); 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 968ab09c1469..2f6bf4a53c57 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 @@ -41,8 +41,7 @@ public class ZipFileTreeTest extends AbstractArchiveFileTreeTest { fileSystem(), directoryFileTreeFactory(), fileHasher(), - TestFiles.cacheFactory(), - () -> userHome); + TestFiles.scopedCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); @Override protected void archiveFileToRoot(TestFile file) { 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 7334affd7ccd..cbd5d3a73472 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 @@ -15,6 +15,7 @@ */ package org.gradle.api.internal.file; +import org.gradle.api.Action; import org.gradle.api.internal.DocumentationRegistry; import org.gradle.api.internal.cache.StringInterner; import org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory; @@ -28,6 +29,14 @@ 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.CacheBuilder; +import org.gradle.cache.CacheOpenException; +import org.gradle.cache.CleanupAction; +import org.gradle.cache.LockOptions; +import org.gradle.cache.PersistentCache; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.cache.scopes.ScopedCache; +import org.gradle.internal.Actions; import org.gradle.internal.Factory; import org.gradle.internal.concurrent.DefaultExecutorFactory; import org.gradle.internal.file.Deleter; @@ -58,7 +67,11 @@ import javax.annotation.Nullable; import java.io.File; import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import static org.gradle.cache.FileLockManager.LockMode.Exclusive; +import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_INSENSITIVE; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_SENSITIVE; import static org.gradle.util.TestUtil.objectFactory; @@ -174,8 +187,7 @@ public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFi documentationRegistry(), taskDependencyFactory(), providerFactory(), - cacheFactory(), - () -> temporaryFileProvider.newTemporaryDirectory("user-home")); + scopedCache(temporaryFileProvider.newTemporaryDirectory("cache-dir"))); } public static ApiTextResourceAdapter.Factory textResourceAdapterFactory(@Nullable TemporaryFileProvider temporaryFileProvider) { @@ -281,7 +293,96 @@ public static TemporaryFileProvider tmpDirTemporaryFileProvider(File baseDir) { return new DefaultTemporaryFileProvider(() -> baseDir); } - public static TestInMemoryCacheFactory cacheFactory() { - return new TestInMemoryCacheFactory(); + public static ScopedCache scopedCache(File cacheDir) { + return new TestScopedCache(cacheDir); + } + + private static final class TestScopedCache implements ScopedCache { + private final File cacheDir; + + private TestScopedCache(File cacheDir) { + this.cacheDir = cacheDir; + } + + @Override + public CacheBuilder cache(String key) { + return new TestCacheBuilder(baseDirForCache(key)); + } + + @Override + public CacheBuilder crossVersionCache(String key) { + return new TestCacheBuilder(baseDirForCrossVersionCache(key)); + } + + @Override + public File getRootDir() { + return cacheDir; + } + + @Override + public File baseDirForCache(String key) { + return new File(cacheDir, "base"); + } + + @Override + public File baseDirForCrossVersionCache(String key) { + return new File(cacheDir, "crossVersion"); + } + } + + private static final class TestCacheBuilder implements CacheBuilder { + private final CacheFactory cacheFactory = new TestInMemoryCacheFactory(); + private final File cacheDir; + private Map properties = Collections.emptyMap(); + private LockTarget lockTarget = LockTarget.DefaultTarget; + private String displayName = "Test Cache"; + private LockOptions lockOptions = mode(Exclusive); + private Action initializer = Actions.doNothing(); + private CleanupAction cleanup = CleanupAction.NO_OP; + + private TestCacheBuilder(File cacheDir) { + this.cacheDir = cacheDir; + } + + @Override + public CacheBuilder withProperties(Map properties) { + this.properties = properties; + return this; + } + + @Override + public CacheBuilder withCrossVersionCache(LockTarget lockTarget) { + this.lockTarget = lockTarget; + return this; + } + + @Override + public CacheBuilder withDisplayName(String displayName) { + this.displayName = displayName; + return this; + } + + @Override + public CacheBuilder withLockOptions(LockOptions lockOptions) { + this.lockOptions = lockOptions; + return this; + } + + @Override + public CacheBuilder withInitializer(Action initializer) { + this.initializer = initializer; + return this; + } + + @Override + public CacheBuilder withCleanup(CleanupAction cleanup) { + this.cleanup = cleanup; + return this; + } + + @Override + public PersistentCache open() throws CacheOpenException { + return cacheFactory.open(cacheDir, displayName, properties, lockTarget, lockOptions, initializer, cleanup); + } } } From f632f2a4b3d97073392b6396ae0f865ab8956860 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 30 Nov 2022 16:24:59 -0500 Subject: [PATCH 014/120] Add tests for simultaneous visit + edit of the same archive --- .../bundling/ArchiveIntegrationTest.groovy | 171 +++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) 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..2515bb378623 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 @@ -939,7 +939,6 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { false | ["file2.txt", "file3.txt"] } - @Issue("") def "zipTree tracks task dependencies"() { given: buildFile """ @@ -961,6 +960,114 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { executedAndNotSkipped ':jar', ':unpackJar' } + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit zip archive differently from two different projects"() { + 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')} + + tasks.register('update', UpdateTask) { + archive = rootProject.file('test.zip') + replacementText = 'modified by project1' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = rootProject.file('test.zip') + beginsWith = 'modified by project1' + } + """ + + file('project2/build.gradle') << """ + ${defineUpdateTask('zip')} + ${defineVerifyTask('zip')} + + tasks.register('update', UpdateTask) { + archive = rootProject.file('test.zip') + replacementText = 'edited by project2' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = rootProject.file('test.zip') + 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"() { + 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')} + + tasks.register('update', UpdateTask) { + archive = rootProject.file('test.tar') + replacementText = 'modified by project1' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = rootProject.file('test.tar') + beginsWith = 'modified by project1' + } + """ + + file('project2/build.gradle') << """ + ${defineUpdateTask('tar')} + ${defineVerifyTask('tar')} + + tasks.register('update', UpdateTask) { + archive = rootProject.file('test.tar') + replacementText = 'edited by project2' + } + + tasks.register('verify', VerifyTask) { + dependsOn tasks.named('update') + archive = rootProject.file('test.tar') + beginsWith = 'edited by project2' + } + """ + + when: + run 'verify' + + then: + result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') + } + private def createTar(String name, Closure cl) { TestFile tarRoot = file("${name}.root") tarRoot.deleteDir() @@ -1000,4 +1107,66 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } """ } + + private String defineUpdateTask(String archiveType) { + return """ + abstract class UpdateTask extends DefaultTask { + @Inject + abstract org.gradle.api.internal.file.FileOperations getFileOperations() + + @InputFile + abstract RegularFileProperty getArchive() + + @Input + abstract Property getReplacementText() + + @TaskAction + void update() { + FileTree tree = fileOperations.${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 { + @Inject + abstract org.gradle.api.internal.file.FileOperations getFileOperations() + + @InputFile + abstract RegularFileProperty getArchive() + + @Input + abstract Property getBeginsWith() + + @TaskAction + void verify() { + FileTree tree = fileOperations.${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()) + } + } + } + """ + } } From fedf7fb143023893690f8e5230a60965afb32b1b Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 30 Nov 2022 16:37:17 -0500 Subject: [PATCH 015/120] Fix test archive file tree to use ScopedCache --- .../file/archive/AbstractArchiveFileTreeSpec.groovy | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy index 882010668edf..313ea866a4dd 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy @@ -22,8 +22,7 @@ import org.gradle.api.internal.file.TestFiles 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.CacheFactory -import org.gradle.initialization.GradleUserHomeDirProvider +import org.gradle.cache.scopes.ScopedCache import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider import org.gradle.util.TestUtil import org.junit.Rule @@ -40,10 +39,7 @@ class AbstractArchiveFileTreeSpec extends Specification { def visitor = Mock(MinimalFileTree.MinimalFileTreeStructureVisitor) def backingFile = tmpDir.createFile("thing.bin") - def fileTree = new TestArchiveFileTree( - TestFiles.cacheFactory(), - () -> tmpDir.createDir("user-home"), - backingFile) + def fileTree = new TestArchiveFileTree(TestFiles.scopedCache(tmpDir.createDir("cache-dir")), backingFile) when: fileTree.visitStructure(visitor, owner) @@ -57,8 +53,8 @@ class AbstractArchiveFileTreeSpec extends Specification { File backingFile final String displayName = "" - TestArchiveFileTree(CacheFactory cacheFactory, GradleUserHomeDirProvider userHomeDirProvider, File backingFile) { - super(cacheFactory, userHomeDirProvider) + TestArchiveFileTree(ScopedCache cache, File backingFile) { + super(cache) this.backingFile = backingFile } From 62b7eb461d67de9c72e50594fd606d2bc790f493 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 30 Nov 2022 17:13:55 -0500 Subject: [PATCH 016/120] First attempt at creating a project scoped cache --- .../internal/file/DefaultFileOperations.java | 4 +-- .../service/scopes/ProjectScopeServices.java | 7 +++++ .../scopes/DefaultProjectScopedCache.java | 31 +++++++++++++++++++ .../cache/scopes/ProjectScopedCache.java | 27 ++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/scopes/DefaultProjectScopedCache.java create mode 100644 subprojects/persistent-cache/src/main/java/org/gradle/cache/scopes/ProjectScopedCache.java 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 37f67a054e8f..b591a3a23250 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 @@ -51,7 +51,7 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; -import org.gradle.cache.scopes.BuildScopedCache; +import org.gradle.cache.scopes.ProjectScopedCache; import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Cast; import org.gradle.internal.Factory; @@ -308,7 +308,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); - BuildScopedCache decompressionCache = services.get(BuildScopedCache.class); + ScopedCache decompressionCache = services.get(ProjectScopedCache.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, 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 22c28508d5ea..49e274110cf1 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 @@ -69,6 +69,9 @@ import org.gradle.api.internal.tasks.TaskStatistics; import org.gradle.api.internal.tasks.properties.TaskScheme; import org.gradle.api.model.ObjectFactory; +import org.gradle.cache.CacheRepository; +import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; +import org.gradle.cache.scopes.ProjectScopedCache; import org.gradle.configuration.ConfigurationTargetIdentifier; import org.gradle.configuration.internal.UserCodeApplicationContext; import org.gradle.configuration.project.DefaultProjectConfigurationActionContainer; @@ -346,4 +349,8 @@ protected ManagedFactoryRegistry createManagedFactoryRegistry(ManagedFactoryRegi new org.gradle.api.internal.file.ManagedFactories.DirectoryPropertyManagedFactory(filePropertyFactory) ); } + + ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository) { + return new DefaultProjectScopedCache(project.getLayout().getBuildDirectory().dir("cache").get().getAsFile(), cacheRepository); + } } 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 { +} From 4a65c9e384f65e1e11b8ea693487fc20df91983f Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Thu, 1 Dec 2022 09:13:03 -0500 Subject: [PATCH 017/120] Use subdirectory for output in test, to avoid conflicts with caches appearing there --- .../gradle/api/tasks/bundling/ArchiveIntegrationTest.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2515bb378623..ad8b9d7e23f3 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 From 2a70ca87ec5d65307a85b043b764b92c050459e2 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Thu, 1 Dec 2022 09:14:15 -0500 Subject: [PATCH 018/120] Use BuildTreeScopedCache for default file operations, use project.fileOperation to grab zip/tar trees that work with project-level caches --- .../api/tasks/bundling/ArchiveIntegrationTest.groovy | 10 ++-------- .../api/internal/file/DefaultFileOperations.java | 4 ++-- .../internal/service/scopes/ProjectScopeServices.java | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) 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 ad8b9d7e23f3..ae5de2021f17 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 @@ -1111,9 +1111,6 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { private String defineUpdateTask(String archiveType) { return """ abstract class UpdateTask extends DefaultTask { - @Inject - abstract org.gradle.api.internal.file.FileOperations getFileOperations() - @InputFile abstract RegularFileProperty getArchive() @@ -1122,7 +1119,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @TaskAction void update() { - FileTree tree = fileOperations.${archiveType}Tree(archive.asFile.get()) + FileTree tree = project.fileOperations.${archiveType}Tree(archive.asFile.get()) tree.visit(new EditingFileVisitor()) } @@ -1142,9 +1139,6 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { private String defineVerifyTask(String archiveType) { return """ abstract class VerifyTask extends DefaultTask { - @Inject - abstract org.gradle.api.internal.file.FileOperations getFileOperations() - @InputFile abstract RegularFileProperty getArchive() @@ -1153,7 +1147,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @TaskAction void verify() { - FileTree tree = fileOperations.${archiveType}Tree(archive.asFile.get()) + FileTree tree = project.fileOperations.${archiveType}Tree(archive.asFile.get()) tree.visit(new VerifyingFileVisitor()) } 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 b591a3a23250..17cadb7b7be9 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 @@ -51,7 +51,7 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; -import org.gradle.cache.scopes.ProjectScopedCache; +import org.gradle.cache.scopes.BuildTreeScopedCache; import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Cast; import org.gradle.internal.Factory; @@ -308,7 +308,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); - ScopedCache decompressionCache = services.get(ProjectScopedCache.class); + ScopedCache decompressionCache = services.get(BuildTreeScopedCache.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, 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 49e274110cf1..214342d29e12 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 @@ -350,7 +350,7 @@ protected ManagedFactoryRegistry createManagedFactoryRegistry(ManagedFactoryRegi ); } - ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository) { + protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository) { return new DefaultProjectScopedCache(project.getLayout().getBuildDirectory().dir("cache").get().getAsFile(), cacheRepository); } } From 8f45332c376fd75ea70e6d2560838f9db27269bf Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Thu, 1 Dec 2022 12:19:07 -0500 Subject: [PATCH 019/120] Test obtaining trees from both ArchiveOperations and project.fileOperations --- .../bundling/ArchiveIntegrationTest.groovy | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) 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 ae5de2021f17..5bc8cfa76ecc 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 @@ -961,7 +961,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit zip archive differently from two different projects"() { + def "can visit and edit zip archive differently from two different projects using ArchiveOperations=#useArchiveOps"() { given: "an archive in the root of a multiproject build" createZip('test.zip') { subdir1 { @@ -976,8 +976,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { and: "where each project edits that same archive differently via a visitor" file('project1/build.gradle') << """ - ${defineUpdateTask('zip')} - ${defineVerifyTask('zip')} + ${defineUpdateTask('zip', useArchiveOps)} + ${defineVerifyTask('zip', useArchiveOps)} tasks.register('update', UpdateTask) { archive = rootProject.file('test.zip') @@ -992,8 +992,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ file('project2/build.gradle') << """ - ${defineUpdateTask('zip')} - ${defineVerifyTask('zip')} + ${defineUpdateTask('zip', useArchiveOps)} + ${defineVerifyTask('zip', useArchiveOps)} tasks.register('update', UpdateTask) { archive = rootProject.file('test.zip') @@ -1012,10 +1012,13 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { then: result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') + + where: + useArchiveOps << [true, false] } @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit tar archive differently from two different projects"() { + def "can visit and edit tar archive differently from two different projects using ArchiveOperations=#useArchiveOps"() { given: "an archive in the root of a multiproject build" createTar('test.tar') { subdir1 { @@ -1030,8 +1033,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { and: "where each project edits that same archive differently via a visitor" file('project1/build.gradle') << """ - ${defineUpdateTask('tar')} - ${defineVerifyTask('tar')} + ${defineUpdateTask('tar', useArchiveOps)} + ${defineVerifyTask('tar', useArchiveOps)} tasks.register('update', UpdateTask) { archive = rootProject.file('test.tar') @@ -1046,8 +1049,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ file('project2/build.gradle') << """ - ${defineUpdateTask('tar')} - ${defineVerifyTask('tar')} + ${defineUpdateTask('tar', useArchiveOps)} + ${defineVerifyTask('tar', useArchiveOps)} tasks.register('update', UpdateTask) { archive = rootProject.file('test.tar') @@ -1066,6 +1069,9 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { then: result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') + + where: + useArchiveOps << [true, false] } private def createTar(String name, Closure cl) { @@ -1108,7 +1114,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ } - private String defineUpdateTask(String archiveType) { + private String defineUpdateTask(String archiveType, boolean useArchiveOps) { return """ abstract class UpdateTask extends DefaultTask { @InputFile @@ -1117,9 +1123,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @Input abstract Property getReplacementText() + ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : ""} + @TaskAction void update() { - FileTree tree = project.fileOperations.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${useArchiveOps ? "archiveOperations" : "project.fileOperations"}.${archiveType}Tree(archive.asFile.get()) tree.visit(new EditingFileVisitor()) } @@ -1136,7 +1144,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ } - private String defineVerifyTask(String archiveType) { + private String defineVerifyTask(String archiveType, boolean useArchiveOps) { return """ abstract class VerifyTask extends DefaultTask { @InputFile @@ -1145,9 +1153,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @Input abstract Property getBeginsWith() + ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : ""} + @TaskAction void verify() { - FileTree tree = project.fileOperations.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${useArchiveOps ? "archiveOperations" : "project.fileOperations"}.${archiveType}Tree(archive.asFile.get()) tree.visit(new VerifyingFileVisitor()) } From 11c6983b848783d8ab7fb953343044796bfdde28 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Thu, 1 Dec 2022 16:48:23 -0500 Subject: [PATCH 020/120] Switch to a cross version cache --- .../api/internal/file/archive/AbstractArchiveFileTree.java | 2 ++ 1 file changed, 2 insertions(+) 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 022a98f85b1b..4602a73be6f4 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,6 +21,7 @@ import org.gradle.api.internal.tasks.TaskDependencyContainer; import org.gradle.api.internal.tasks.TaskDependencyResolveContext; import org.gradle.api.provider.Provider; +import org.gradle.cache.CacheBuilder; import org.gradle.cache.FileLockManager; import org.gradle.cache.PersistentCache; import org.gradle.cache.scopes.ScopedCache; @@ -37,6 +38,7 @@ public abstract class AbstractArchiveFileTree implements FileSystemMirroringFile protected AbstractArchiveFileTree(ScopedCache cacheBuilder) { this.expansionCache = cacheBuilder.cache(EXPANSION_CACHE_KEY) + .withCrossVersionCache(CacheBuilder.LockTarget.CacheDirectory) .withDisplayName(EXPANSION_CACHE_NAME) .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) .open(); From ce3308694a74d2cb49a48cb243e6d9dbeeabb92a Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Thu, 1 Dec 2022 16:49:08 -0500 Subject: [PATCH 021/120] Use prefix + hash only to id decompressed archives, so that the same file with different file names will use the same cache entry --- .../java/org/gradle/api/internal/file/archive/TarFileTree.java | 2 +- .../java/org/gradle/api/internal/file/archive/ZipFileTree.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 bf12c6839d61..ceb60b71aaf6 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 @@ -124,7 +124,7 @@ public Provider getBackingFileProvider() { private File getExpandedDir() { File tarFile = tarFileProvider.get(); HashCode fileHash = hashFile(tarFile); - String expandedDirName = tarFile.getName() + "_" + fileHash; + String expandedDirName = "tar_" + fileHash; return new File(expansionCache.getBaseDir(), expandedDirName); } 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 7371a2ce2f49..bad6a480ea5b 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 @@ -125,7 +125,7 @@ public Provider getBackingFileProvider() { private File getExpandedDir() { File zipFile = fileProvider.get(); - String expandedDirName = zipFile.getName() + "_" + fileHasher.hash(zipFile); + String expandedDirName = "zip_" + fileHasher.hash(zipFile); return new File(expansionCache.getBaseDir(), expandedDirName); } From 874e9bc7674e69f2f6b779b6d69cce3be21641a1 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Thu, 1 Dec 2022 16:51:20 -0500 Subject: [PATCH 022/120] Add test to run 2 tasks in same project via 2 different gradle processes and check that modifications of cached decompressed files are atomic --- .../bundling/ArchiveIntegrationTest.groovy | 192 +++++++++++++++++- 1 file changed, 191 insertions(+), 1 deletion(-) 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 5bc8cfa76ecc..3866c69daf93 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 @@ -20,11 +20,15 @@ import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.tasks.Copy import org.gradle.integtests.fixtures.AbstractIntegrationSpec import org.gradle.integtests.fixtures.archives.TestReproducibleArchives +import org.gradle.integtests.fixtures.executer.GradleContextualExecuter import org.gradle.test.fixtures.archive.ArchiveTestFixture import org.gradle.test.fixtures.archive.TarTestFixture import org.gradle.test.fixtures.archive.ZipTestFixture import org.gradle.test.fixtures.file.TestFile +import org.gradle.test.fixtures.server.http.BlockingHttpServer import org.hamcrest.CoreMatchers +import org.junit.Rule +import spock.lang.IgnoreIf import spock.lang.Issue import static org.hamcrest.CoreMatchers.equalTo @@ -33,6 +37,9 @@ import static org.hamcrest.CoreMatchers.equalTo class ArchiveIntegrationTest extends AbstractIntegrationSpec { private final static DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry() + @Rule + BlockingHttpServer server = new BlockingHttpServer() + def canCopyFromAZip() { given: createZip('test.zip') { @@ -1074,6 +1081,156 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { useArchiveOps << [true, false] } + @IgnoreIf({ GradleContextualExecuter.embedded }) + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit tar archive differently from two different tasks in the same project run in two simultaneous processes"() { + given: "a started server which can be used to cause the tasks 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 task 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 build script which defines two tasks that edit the same tar archive differently, and a verification task that checked that all files contain the same content" + buildFile << """ + ${defineUpdateTask('tar')} + ${defineSameContentsTask('tar')} + + tasks.register('wait1') { + doLast { + ${server.callFromBuild('wait1')} + } + } + + tasks.register('wait2') { + doLast { + ${server.callFromBuild('wait2')} + } + } + + tasks.register('update1', UpdateTask) { + dependsOn tasks.named('wait1') + archive = rootProject.file('test.tar') + replacementText = 'modification 1' + } + + tasks.register('update2', UpdateTask) { + dependsOn tasks.named('wait2') + archive = rootProject.file('test.tar') + replacementText = 'modification 2' + } + + tasks.register('verify', SameContentsTask) { + archive = rootProject.file('test.tar') + } + """ + server.expectConcurrent('wait1', 'wait2') + + when: "the tasks are run in two processes" + def handle1 = executer.withTasks('update1').start() + def handle2 = executer.withTasks('update2').start() + + and: "they both complete" + def result1 = handle1.waitForFinish() + def result2 = handle2.waitForFinish() + + then: "both update tasks were executed" + result1.assertTasksExecuted(':wait1', ':update1') + result2.assertTasksExecuted(':wait2', ':update2') + + and: "verification can complete successfully, ensuring that each task edited the archive atomically" + executer.withTasks('verify').run() + + cleanup: + handle1?.abort() + handle2?.abort() + server.stop() + } + + @IgnoreIf({ GradleContextualExecuter.embedded }) + @Issue("https://github.com/gradle/gradle/issues/22685") + def "can visit and edit zip archive differently from two different tasks in the same project run in two simultaneous processes"() { + given: "a started server which can be used to cause the tasks 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 task 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 build script which defines two tasks that edit the same zip archive differently, and a verification task that checked that all files contain the same content" + buildFile << """ + ${defineUpdateTask('zip')} + ${defineSameContentsTask('zip')} + + tasks.register('wait1') { + doLast { + ${server.callFromBuild('wait1')} + } + } + + tasks.register('wait2') { + doLast { + ${server.callFromBuild('wait2')} + } + } + + tasks.register('update1', UpdateTask) { + dependsOn tasks.named('wait1') + archive = rootProject.file('test.zip') + replacementText = 'modification 1' + } + + tasks.register('update2', UpdateTask) { + dependsOn tasks.named('wait2') + archive = rootProject.file('test.zip') + replacementText = 'modification 2' + } + + tasks.register('verify', SameContentsTask) { + archive = rootProject.file('test.zip') + } + """ + server.expectConcurrent('wait1', 'wait2') + + when: "the tasks are run in two processes" + def handle1 = executer.withTasks('update1').start() + def handle2 = executer.withTasks('update2').start() + + and: "they both complete" + def result1 = handle1.waitForFinish() + def result2 = handle2.waitForFinish() + + then: "both update tasks were executed" + result1.assertTasksExecuted(':wait1', ':update1') + result2.assertTasksExecuted(':wait2', ':update2') + + and: "verification can complete successfully, ensuring that each task edited the archive atomically" + executer.withTasks('verify').run() + + cleanup: + handle1?.abort() + handle2?.abort() + server.stop() + } + private def createTar(String name, Closure cl) { TestFile tarRoot = file("${name}.root") tarRoot.deleteDir() @@ -1114,7 +1271,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ } - private String defineUpdateTask(String archiveType, boolean useArchiveOps) { + private String defineUpdateTask(String archiveType, boolean useArchiveOps = true) { return """ abstract class UpdateTask extends DefaultTask { @InputFile @@ -1173,4 +1330,37 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } """ } + + private String defineSameContentsTask(String archiveType, boolean useArchiveOps = true) { + return """ + abstract class SameContentsTask extends DefaultTask { + @InputFile + abstract RegularFileProperty getArchive() + + ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : ""} + + @TaskAction + void verify() { + FileTree tree = ${useArchiveOps ? "archiveOperations" : "project.fileOperations"}.${archiveType}Tree(archive.asFile.get()) + tree.visit(new VerifyingFileVisitor()) + } + + private 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 + } + } + } + } + """ + } } From b3443ae22d3974ec1c413237a3d861e1a8f5067e Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 08:29:50 -0500 Subject: [PATCH 023/120] Move creation of ProjectScopedCache into WorkerSharedProjectScopeServices to make it more broadly available Caches created under ./gradle//compressed-file-expansion (non-project level) or //.gradle//compressed-file-expansion (project-level). --- .../service/scopes/ProjectScopeServices.java | 7 ------- .../scopes/WorkerSharedProjectScopeServices.java | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) 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 214342d29e12..22c28508d5ea 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 @@ -69,9 +69,6 @@ import org.gradle.api.internal.tasks.TaskStatistics; import org.gradle.api.internal.tasks.properties.TaskScheme; import org.gradle.api.model.ObjectFactory; -import org.gradle.cache.CacheRepository; -import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; -import org.gradle.cache.scopes.ProjectScopedCache; import org.gradle.configuration.ConfigurationTargetIdentifier; import org.gradle.configuration.internal.UserCodeApplicationContext; import org.gradle.configuration.project.DefaultProjectConfigurationActionContainer; @@ -349,8 +346,4 @@ protected ManagedFactoryRegistry createManagedFactoryRegistry(ManagedFactoryRegi new org.gradle.api.internal.file.ManagedFactories.DirectoryPropertyManagedFactory(filePropertyFactory) ); } - - protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository) { - return new DefaultProjectScopedCache(project.getLayout().getBuildDirectory().dir("cache").get().getAsFile(), cacheRepository); - } } 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 22a6992f4500..6e58872d273a 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 @@ -44,6 +44,12 @@ 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.CacheFactory; +import org.gradle.cache.internal.DefaultCacheRepository; +import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; +import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; +import org.gradle.cache.scopes.ProjectScopedCache; import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; @@ -57,6 +63,7 @@ import org.gradle.process.ExecOperations; import org.gradle.process.internal.DefaultExecOperations; import org.gradle.process.internal.ExecFactory; +import org.gradle.util.GradleVersion; import java.io.File; @@ -148,4 +155,12 @@ 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(CacheRepository cacheRepository) { + return new DefaultProjectScopedCache(new File(projectDir, ".gradle"), cacheRepository); + } + + protected CacheRepository createCacheRepository(CacheFactory cacheFactory) { + return new DefaultCacheRepository(new DefaultCacheScopeMapping(new File(projectDir, ".gradle"), GradleVersion.current()), cacheFactory); + } } From 8a465c02160b4348e04f860f804af9f1ef5897d9 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:01:03 -0500 Subject: [PATCH 024/120] Add clarifying comments to test --- .../archive/AbstractArchiveFileTreeTest.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) 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 index ed1ddc5f5f06..d117dad3b191 100644 --- 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 @@ -51,7 +51,6 @@ 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 userHome = tempDirProvider.getTestDirectory().file("user-home"); protected final TestFile rootDir = tempDirProvider.getTestDirectory().file("root"); protected abstract TestFile getArchiveFile(); @@ -65,6 +64,7 @@ public void testConcurrentArchiveVisiting() throws InterruptedException { 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<>(); @@ -103,14 +103,23 @@ public void visitFile(FileVisitDetails fileDetails) { visitors.add(new CountingVisitor()); } - List>> callables = visitors.stream().map(v -> (Callable>) () -> { - getTree().visit(v); - return v.getActualCounts(); + // 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)); From e75d5b252591b5d03eb828fa7badbcb4ec90b764 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:01:30 -0500 Subject: [PATCH 025/120] Use default lock behavior when locking the cache --- .../api/internal/file/archive/AbstractArchiveFileTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4602a73be6f4..882be4152f76 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 @@ -38,7 +38,7 @@ public abstract class AbstractArchiveFileTree implements FileSystemMirroringFile protected AbstractArchiveFileTree(ScopedCache cacheBuilder) { this.expansionCache = cacheBuilder.cache(EXPANSION_CACHE_KEY) - .withCrossVersionCache(CacheBuilder.LockTarget.CacheDirectory) + .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) .withDisplayName(EXPANSION_CACHE_NAME) .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) .open(); From 958323e8bfe27c78a265453ed9816cb6b7ed5cc4 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:31:05 -0500 Subject: [PATCH 026/120] Allow multiple simultaneous readers of decompression caches, use default locking --- .../api/internal/file/archive/AbstractArchiveFileTree.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 882be4152f76..3ca55894ac24 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 @@ -38,9 +38,9 @@ public abstract class AbstractArchiveFileTree implements FileSystemMirroringFile protected AbstractArchiveFileTree(ScopedCache cacheBuilder) { this.expansionCache = cacheBuilder.cache(EXPANSION_CACHE_KEY) - .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) .withDisplayName(EXPANSION_CACHE_NAME) - .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) + .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) + .withLockOptions(mode(FileLockManager.LockMode.Shared)) .open(); } From e9940d1102ba365b0a030612547ca8100b716b29 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:32:05 -0500 Subject: [PATCH 027/120] Fix InMemoryCacheFactory to properly synchronize, as required by contract --- .../testfixtures/internal/TestInMemoryCacheFactory.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 cb1399eeb270..222ec59af555 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 @@ -149,7 +149,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 From 162ccbbd2c1f8baf39b59566a4677f490976b019 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:33:21 -0500 Subject: [PATCH 028/120] Update test scoped cache creation to use shared cache, to better imitate cache it's doubling in tests Also make dir name used by cache more complete. --- .../groovy/org/gradle/api/internal/file/TestFiles.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 cbd5d3a73472..b2ab30945ceb 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 @@ -35,6 +35,7 @@ import org.gradle.cache.LockOptions; import org.gradle.cache.PersistentCache; import org.gradle.cache.internal.CacheFactory; +import org.gradle.cache.internal.scopes.DefaultGlobalScopedCache; import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Actions; import org.gradle.internal.Factory; @@ -70,7 +71,7 @@ import java.util.Collections; import java.util.Map; -import static org.gradle.cache.FileLockManager.LockMode.Exclusive; +import static org.gradle.cache.FileLockManager.LockMode.Shared; import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_INSENSITIVE; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_SENSITIVE; @@ -326,7 +327,7 @@ public File baseDirForCache(String key) { @Override public File baseDirForCrossVersionCache(String key) { - return new File(cacheDir, "crossVersion"); + return new File(cacheDir, "cross-version-base"); } } @@ -336,7 +337,7 @@ private static final class TestCacheBuilder implements CacheBuilder { private Map properties = Collections.emptyMap(); private LockTarget lockTarget = LockTarget.DefaultTarget; private String displayName = "Test Cache"; - private LockOptions lockOptions = mode(Exclusive); + private LockOptions lockOptions = mode(Shared); private Action initializer = Actions.doNothing(); private CleanupAction cleanup = CleanupAction.NO_OP; From 2c5ed8a7a645df8c47c7b5c531d9f5130e8ccb88 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:44:25 -0500 Subject: [PATCH 029/120] Revert to using exclusive cache locking This avoids "Caused by: java.lang.UnsupportedOperationException: Cannot escalate a shared lock to an exclusive lock. This is not yet supported." errors. --- .../api/internal/file/archive/AbstractArchiveFileTree.java | 2 +- .../groovy/org/gradle/api/internal/file/TestFiles.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) 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 3ca55894ac24..5244e828ccc8 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 @@ -40,7 +40,7 @@ protected AbstractArchiveFileTree(ScopedCache cacheBuilder) { this.expansionCache = cacheBuilder.cache(EXPANSION_CACHE_KEY) .withDisplayName(EXPANSION_CACHE_NAME) .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) - .withLockOptions(mode(FileLockManager.LockMode.Shared)) + .withLockOptions(mode(FileLockManager.LockMode.Exclusive)) .open(); } 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 b2ab30945ceb..efd5088aaa3b 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 @@ -35,7 +35,6 @@ import org.gradle.cache.LockOptions; import org.gradle.cache.PersistentCache; import org.gradle.cache.internal.CacheFactory; -import org.gradle.cache.internal.scopes.DefaultGlobalScopedCache; import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Actions; import org.gradle.internal.Factory; @@ -71,7 +70,7 @@ import java.util.Collections; import java.util.Map; -import static org.gradle.cache.FileLockManager.LockMode.Shared; +import static org.gradle.cache.FileLockManager.LockMode.Exclusive; import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_INSENSITIVE; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_SENSITIVE; @@ -337,7 +336,7 @@ private static final class TestCacheBuilder implements CacheBuilder { private Map properties = Collections.emptyMap(); private LockTarget lockTarget = LockTarget.DefaultTarget; private String displayName = "Test Cache"; - private LockOptions lockOptions = mode(Shared); + private LockOptions lockOptions = mode(Exclusive); private Action initializer = Actions.doNothing(); private CleanupAction cleanup = CleanupAction.NO_OP; From acaa8081fde40308b6793bbf136c83c00ce10dab Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 09:59:30 -0500 Subject: [PATCH 030/120] Renamed and commented code for better clarity --- .../api/internal/file/DefaultFileOperations.java | 14 +++++++------- .../file/archive/AbstractArchiveFileTree.java | 12 +++++++++--- .../scopes/WorkerSharedProjectScopeServices.java | 4 ++-- .../archive/AbstractArchiveFileTreeSpec.groovy | 6 +++--- .../org/gradle/api/internal/file/TestFiles.java | 16 ++++++++-------- 5 files changed, 29 insertions(+), 23 deletions(-) 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 17cadb7b7be9..028774d3d23c 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 @@ -89,7 +89,7 @@ public class DefaultFileOperations implements FileOperations { private final FileCollectionFactory fileCollectionFactory; private final TaskDependencyFactory taskDependencyFactory; private final ProviderFactory providers; - private final ScopedCache cacheBuilder; + private final ScopedCache decompressionCacheFactory; public DefaultFileOperations( FileResolver fileResolver, @@ -107,7 +107,7 @@ public DefaultFileOperations( DocumentationRegistry documentationRegistry, TaskDependencyFactory taskDependencyFactory, ProviderFactory providers, - ScopedCache cacheBuilder + ScopedCache decompressionCacheFactory ) { this.fileCollectionFactory = fileCollectionFactory; this.fileResolver = fileResolver; @@ -133,7 +133,7 @@ public DefaultFileOperations( ); this.fileSystem = fileSystem; this.deleter = deleter; - this.cacheBuilder = cacheBuilder; + this.decompressionCacheFactory = decompressionCacheFactory; } @Override @@ -183,7 +183,7 @@ public ConfigurableFileTree fileTree(Map args) { @Override public FileTreeInternal zipTree(Object zipPath) { Provider fileProvider = asFileProvider(zipPath); - return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, cacheBuilder), taskDependencyFactory, patternSetFactory); + return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory), taskDependencyFactory, patternSetFactory); } @Override @@ -198,7 +198,7 @@ public FileTreeInternal tarTree(Object tarPath) { } }); - TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, cacheBuilder); + TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory); return new FileTreeAdapter(tarTree, taskDependencyFactory, patternSetFactory); } @@ -308,7 +308,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); - ScopedCache decompressionCache = services.get(BuildTreeScopedCache.class); + ScopedCache decompressionCacheFactory = services.get(BuildTreeScopedCache.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, @@ -334,6 +334,6 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File documentationRegistry, taskDependencyFactory, providers, - decompressionCache); + 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 5244e828ccc8..cb9714577cc3 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 @@ -30,14 +30,20 @@ import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; -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 a managed cache directory, so that access to the archive's contents + * are only permitted one at a time. The cache directory is a Gradle cross version cache. + */ +/* package */ abstract class AbstractArchiveFileTree implements FileSystemMirroringFileTree, TaskDependencyContainer { private static final String EXPANSION_CACHE_KEY = "compressed-file-expansion"; private static final String EXPANSION_CACHE_NAME = "Compressed Files Expansion Cache"; protected final PersistentCache expansionCache; - protected AbstractArchiveFileTree(ScopedCache cacheBuilder) { - this.expansionCache = cacheBuilder.cache(EXPANSION_CACHE_KEY) + protected AbstractArchiveFileTree(ScopedCache decompressionCacheFactory) { + this.expansionCache = decompressionCacheFactory.cache(EXPANSION_CACHE_KEY) .withDisplayName(EXPANSION_CACHE_NAME) .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) .withLockOptions(mode(FileLockManager.LockMode.Exclusive)) 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 6e58872d273a..8862d51066ad 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 @@ -103,7 +103,7 @@ protected DefaultFileOperations createFileOperations( DocumentationRegistry documentationRegistry, ProviderFactory providers, TaskDependencyFactory taskDependencyFactory, - ScopedCache cacheBuilder + ScopedCache decompressionCacheFactory ) { return new DefaultFileOperations( fileResolver, @@ -121,7 +121,7 @@ protected DefaultFileOperations createFileOperations( documentationRegistry, taskDependencyFactory, providers, - cacheBuilder); + decompressionCacheFactory); } protected FileSystemOperations createFileSystemOperations(Instantiator instantiator, FileOperations fileOperations) { diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy index 313ea866a4dd..b777640532d3 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy @@ -32,7 +32,7 @@ import spock.lang.Specification */ class AbstractArchiveFileTreeSpec extends Specification { @Rule - public final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()) + TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass()) def "visits structure when backing file is known"() { def owner = Stub(FileTreeInternal) @@ -53,8 +53,8 @@ class AbstractArchiveFileTreeSpec extends Specification { File backingFile final String displayName = "" - TestArchiveFileTree(ScopedCache cache, File backingFile) { - super(cache) + TestArchiveFileTree(ScopedCache decompressionCacheFactory, File backingFile) { + super(decompressionCacheFactory) this.backingFile = backingFile } 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 efd5088aaa3b..d6a079fa342b 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 @@ -294,24 +294,24 @@ public static TemporaryFileProvider tmpDirTemporaryFileProvider(File baseDir) { } public static ScopedCache scopedCache(File cacheDir) { - return new TestScopedCache(cacheDir); + return new TestInMemoryScopedCache(cacheDir); } - private static final class TestScopedCache implements ScopedCache { + private static final class TestInMemoryScopedCache implements ScopedCache { private final File cacheDir; - private TestScopedCache(File cacheDir) { + private TestInMemoryScopedCache(File cacheDir) { this.cacheDir = cacheDir; } @Override public CacheBuilder cache(String key) { - return new TestCacheBuilder(baseDirForCache(key)); + return new TestInMemoryCacheBuilder(baseDirForCache(key)); } @Override public CacheBuilder crossVersionCache(String key) { - return new TestCacheBuilder(baseDirForCrossVersionCache(key)); + return new TestInMemoryCacheBuilder(baseDirForCrossVersionCache(key)); } @Override @@ -330,17 +330,17 @@ public File baseDirForCrossVersionCache(String key) { } } - private static final class TestCacheBuilder implements CacheBuilder { + private static final class TestInMemoryCacheBuilder implements CacheBuilder { private final CacheFactory cacheFactory = new TestInMemoryCacheFactory(); private final File cacheDir; private Map properties = Collections.emptyMap(); private LockTarget lockTarget = LockTarget.DefaultTarget; - private String displayName = "Test Cache"; + private String displayName = "Test In Memory Cache"; private LockOptions lockOptions = mode(Exclusive); private Action initializer = Actions.doNothing(); private CleanupAction cleanup = CleanupAction.NO_OP; - private TestCacheBuilder(File cacheDir) { + private TestInMemoryCacheBuilder(File cacheDir) { this.cacheDir = cacheDir; } From 38b4af5cb226d60cfa00a9bc616349ed8ca602db Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 10:28:19 -0500 Subject: [PATCH 031/120] Fix configuration cache issues with project access at task time in tests --- .../bundling/ArchiveIntegrationTest.groovy | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) 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 3866c69daf93..6a90125e1e97 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 @@ -986,14 +986,16 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { ${defineUpdateTask('zip', useArchiveOps)} ${defineVerifyTask('zip', useArchiveOps)} + def theArchive = rootProject.file('test.zip') + tasks.register('update', UpdateTask) { - archive = rootProject.file('test.zip') + archive = theArchive replacementText = 'modified by project1' } tasks.register('verify', VerifyTask) { dependsOn tasks.named('update') - archive = rootProject.file('test.zip') + archive = theArchive beginsWith = 'modified by project1' } """ @@ -1002,14 +1004,16 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { ${defineUpdateTask('zip', useArchiveOps)} ${defineVerifyTask('zip', useArchiveOps)} + def theArchive = rootProject.file('test.zip') + tasks.register('update', UpdateTask) { - archive = rootProject.file('test.zip') + archive = theArchive replacementText = 'edited by project2' } tasks.register('verify', VerifyTask) { dependsOn tasks.named('update') - archive = rootProject.file('test.zip') + archive = theArchive beginsWith = 'edited by project2' } """ @@ -1043,14 +1047,16 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { ${defineUpdateTask('tar', useArchiveOps)} ${defineVerifyTask('tar', useArchiveOps)} + def theArchive = rootProject.file('test.tar') + tasks.register('update', UpdateTask) { - archive = rootProject.file('test.tar') + archive = theArchive replacementText = 'modified by project1' } tasks.register('verify', VerifyTask) { dependsOn tasks.named('update') - archive = rootProject.file('test.tar') + archive = theArchive beginsWith = 'modified by project1' } """ @@ -1059,14 +1065,16 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { ${defineUpdateTask('tar', useArchiveOps)} ${defineVerifyTask('tar', useArchiveOps)} + def theArchive = rootProject.file('test.tar') + tasks.register('update', UpdateTask) { - archive = rootProject.file('test.tar') + archive = theArchive replacementText = 'edited by project2' } tasks.register('verify', VerifyTask) { dependsOn tasks.named('update') - archive = rootProject.file('test.tar') + archive = theArchive beginsWith = 'edited by project2' } """ @@ -1117,15 +1125,17 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } } + def theArchive = rootProject.file('test.tar') + tasks.register('update1', UpdateTask) { dependsOn tasks.named('wait1') - archive = rootProject.file('test.tar') + archive = theArchive replacementText = 'modification 1' } tasks.register('update2', UpdateTask) { dependsOn tasks.named('wait2') - archive = rootProject.file('test.tar') + archive = theArchive replacementText = 'modification 2' } @@ -1192,15 +1202,17 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } } + def theArchive = rootProject.file('test.zip') + tasks.register('update1', UpdateTask) { dependsOn tasks.named('wait1') - archive = rootProject.file('test.zip') + archive = theArchive replacementText = 'modification 1' } tasks.register('update2', UpdateTask) { dependsOn tasks.named('wait2') - archive = rootProject.file('test.zip') + archive = theArchive replacementText = 'modification 2' } @@ -1280,11 +1292,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @Input abstract Property getReplacementText() - ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : ""} + ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : "private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations"} @TaskAction void update() { - FileTree tree = ${useArchiveOps ? "archiveOperations" : "project.fileOperations"}.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${useArchiveOps ? "archiveOperations" : "fileOperations"}.${archiveType}Tree(archive.asFile.get()) tree.visit(new EditingFileVisitor()) } @@ -1310,11 +1322,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @Input abstract Property getBeginsWith() - ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : ""} + ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : "private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations"} @TaskAction void verify() { - FileTree tree = ${useArchiveOps ? "archiveOperations" : "project.fileOperations"}.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${useArchiveOps ? "archiveOperations" : "fileOperations"}.${archiveType}Tree(archive.asFile.get()) tree.visit(new VerifyingFileVisitor()) } @@ -1337,11 +1349,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @InputFile abstract RegularFileProperty getArchive() - ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : ""} + ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : "private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations"} @TaskAction void verify() { - FileTree tree = ${useArchiveOps ? "archiveOperations" : "project.fileOperations"}.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${useArchiveOps ? "archiveOperations" : "fileOperations"}.${archiveType}Tree(archive.asFile.get()) tree.visit(new VerifyingFileVisitor()) } From c11c166b79b72fc114449daaa05704b55d989cf0 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 13:02:36 -0500 Subject: [PATCH 032/120] Create a new DecompressionCache service and inject it directly, rather than injecting the factory to create a cache - Extract cache-related testing factory methods to new class. - The cache should automatically be closed at the proper time now - no more issues on Windows with unable to delete dirs. --- .../internal/file/DefaultFileOperations.java | 17 +- .../file/archive/AbstractArchiveFileTree.java | 23 +-- .../internal/file/archive/TarFileTree.java | 10 +- .../internal/file/archive/ZipFileTree.java | 10 +- .../buildtree/BuildTreeScopeServices.java | 7 + .../WorkerSharedProjectScopeServices.java | 11 +- .../file/archive/TarFileTreeTest.java | 7 +- .../file/archive/ZipFileTreeTest.java | 4 +- .../gradle/api/internal/file/TestFiles.java | 109 +---------- .../org/gradle/cache/internal/TestCaches.java | 173 ++++++++++++++++++ .../cache/internal/DecompressionCache.java | 32 ++++ .../internal/DefaultDecompressionCache.java | 85 +++++++++ 12 files changed, 336 insertions(+), 152 deletions(-) create mode 100644 subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java create mode 100644 subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCache.java create mode 100644 subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java 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 028774d3d23c..07f8c99659ac 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 @@ -51,8 +51,7 @@ import org.gradle.api.tasks.WorkResult; import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; -import org.gradle.cache.scopes.BuildTreeScopedCache; -import org.gradle.cache.scopes.ScopedCache; +import org.gradle.cache.internal.DecompressionCache; import org.gradle.internal.Cast; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; @@ -89,7 +88,7 @@ public class DefaultFileOperations implements FileOperations { private final FileCollectionFactory fileCollectionFactory; private final TaskDependencyFactory taskDependencyFactory; private final ProviderFactory providers; - private final ScopedCache decompressionCacheFactory; + private final DecompressionCache decompressionCache; public DefaultFileOperations( FileResolver fileResolver, @@ -107,7 +106,7 @@ public DefaultFileOperations( DocumentationRegistry documentationRegistry, TaskDependencyFactory taskDependencyFactory, ProviderFactory providers, - ScopedCache decompressionCacheFactory + DecompressionCache decompressionCache ) { this.fileCollectionFactory = fileCollectionFactory; this.fileResolver = fileResolver; @@ -133,7 +132,7 @@ public DefaultFileOperations( ); this.fileSystem = fileSystem; this.deleter = deleter; - this.decompressionCacheFactory = decompressionCacheFactory; + this.decompressionCache = decompressionCache; } @Override @@ -183,7 +182,7 @@ public ConfigurableFileTree fileTree(Map args) { @Override public FileTreeInternal zipTree(Object zipPath) { Provider fileProvider = asFileProvider(zipPath); - return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory), taskDependencyFactory, patternSetFactory); + return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, decompressionCache), taskDependencyFactory, patternSetFactory); } @Override @@ -198,7 +197,7 @@ public FileTreeInternal tarTree(Object tarPath) { } }); - TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory); + TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, decompressionCache); return new FileTreeAdapter(tarTree, taskDependencyFactory, patternSetFactory); } @@ -308,7 +307,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); - ScopedCache decompressionCacheFactory = services.get(BuildTreeScopedCache.class); + DecompressionCache decompressionCache = services.get(DecompressionCache.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, @@ -334,6 +333,6 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File documentationRegistry, taskDependencyFactory, providers, - decompressionCacheFactory); + decompressionCache); } } 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 cb9714577cc3..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,33 +21,20 @@ import org.gradle.api.internal.tasks.TaskDependencyContainer; import org.gradle.api.internal.tasks.TaskDependencyResolveContext; import org.gradle.api.provider.Provider; -import org.gradle.cache.CacheBuilder; -import org.gradle.cache.FileLockManager; -import org.gradle.cache.PersistentCache; -import org.gradle.cache.scopes.ScopedCache; +import org.gradle.cache.internal.DecompressionCache; import java.io.File; -import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; - /** * 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 a managed cache directory, so that access to the archive's contents - * are only permitted one at a time. The cache directory is a Gradle cross version cache. + * Will decompress the archive file to the given cache. */ /* package */ abstract class AbstractArchiveFileTree implements FileSystemMirroringFileTree, TaskDependencyContainer { - private static final String EXPANSION_CACHE_KEY = "compressed-file-expansion"; - private static final String EXPANSION_CACHE_NAME = "Compressed Files Expansion Cache"; - - protected final PersistentCache expansionCache; + protected final DecompressionCache decompressionCache; - protected AbstractArchiveFileTree(ScopedCache decompressionCacheFactory) { - this.expansionCache = decompressionCacheFactory.cache(EXPANSION_CACHE_KEY) - .withDisplayName(EXPANSION_CACHE_NAME) - .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) - .withLockOptions(mode(FileLockManager.LockMode.Exclusive)) - .open(); + protected AbstractArchiveFileTree(DecompressionCache decompressionCache) { + this.decompressionCache = decompressionCache; } abstract protected Provider getBackingFileProvider(); 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 ceb60b71aaf6..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 @@ -26,7 +26,7 @@ import org.gradle.api.provider.Provider; import org.gradle.api.resources.ResourceException; import org.gradle.api.resources.internal.ReadableResourceInternal; -import org.gradle.cache.scopes.ScopedCache; +import org.gradle.cache.internal.DecompressionCache; import org.gradle.internal.file.Chmod; import org.gradle.internal.hash.FileHasher; import org.gradle.internal.hash.HashCode; @@ -53,8 +53,8 @@ public TarFileTree( Chmod chmod, DirectoryFileTreeFactory directoryFileTreeFactory, FileHasher fileHasher, - ScopedCache cacheBuilder) { - super(cacheBuilder); + DecompressionCache decompressionCache) { + super(decompressionCache); this.tarFileProvider = tarFileProvider; this.resource = resource; this.chmod = chmod; @@ -74,7 +74,7 @@ public DirectoryFileTree getMirror() { @Override public void visit(FileVisitor visitor) { - expansionCache.useCache(() -> { + decompressionCache.useCache(() -> { InputStream inputStream; try { inputStream = new BufferedInputStream(resource.get().read()); @@ -125,7 +125,7 @@ private File getExpandedDir() { File tarFile = tarFileProvider.get(); HashCode fileHash = hashFile(tarFile); String expandedDirName = "tar_" + fileHash; - return new File(expansionCache.getBaseDir(), expandedDirName); + return new File(decompressionCache.getBaseDir(), expandedDirName); } private HashCode hashFile(File tarFile) { 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 bad6a480ea5b..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 @@ -24,7 +24,7 @@ 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.scopes.ScopedCache; +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; @@ -52,9 +52,9 @@ public ZipFileTree( Chmod chmod, DirectoryFileTreeFactory directoryFileTreeFactory, FileHasher fileHasher, - ScopedCache cacheBuilder + DecompressionCache decompressionCache ) { - super(cacheBuilder); + super(decompressionCache); this.fileProvider = zipFile; this.chmod = chmod; this.directoryFileTreeFactory = directoryFileTreeFactory; @@ -78,7 +78,7 @@ public DirectoryFileTree getMirror() { @Override public void visit(FileVisitor visitor) { - expansionCache.useCache(() -> { + decompressionCache.useCache(() -> { File zipFile = fileProvider.get(); if (!zipFile.exists()) { throw new InvalidUserDataException(format("Cannot expand %s as it does not exist.", getDisplayName())); @@ -126,7 +126,7 @@ public Provider getBackingFileProvider() { private File getExpandedDir() { File zipFile = fileProvider.get(); String expandedDirName = "zip_" + fileHasher.hash(zipFile); - return new File(expansionCache.getBaseDir(), expandedDirName); + return new File(decompressionCache.getBaseDir(), expandedDirName); } private static final class DetailsImpl extends AbstractArchiveFileTreeElement { 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..3e3d75198b22 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeScopeServices.java +++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildTreeScopeServices.java @@ -21,6 +21,9 @@ 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.DecompressionCache; +import org.gradle.cache.internal.DefaultDecompressionCache; +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 DecompressionCache createDecompressionCache(BuildTreeScopedCache cacheFactory) { + return new DefaultDecompressionCache(cacheFactory); + } } 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 8862d51066ad..3d1b94c083c5 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 @@ -46,11 +46,12 @@ import org.gradle.api.tasks.util.PatternSet; import org.gradle.cache.CacheRepository; import org.gradle.cache.internal.CacheFactory; +import org.gradle.cache.internal.DecompressionCache; import org.gradle.cache.internal.DefaultCacheRepository; +import org.gradle.cache.internal.DefaultDecompressionCache; import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; import org.gradle.cache.scopes.ProjectScopedCache; -import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -103,7 +104,7 @@ protected DefaultFileOperations createFileOperations( DocumentationRegistry documentationRegistry, ProviderFactory providers, TaskDependencyFactory taskDependencyFactory, - ScopedCache decompressionCacheFactory + DecompressionCache decompressionCache ) { return new DefaultFileOperations( fileResolver, @@ -121,7 +122,7 @@ protected DefaultFileOperations createFileOperations( documentationRegistry, taskDependencyFactory, providers, - decompressionCacheFactory); + decompressionCache); } protected FileSystemOperations createFileSystemOperations(Instantiator instantiator, FileOperations fileOperations) { @@ -163,4 +164,8 @@ protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepos protected CacheRepository createCacheRepository(CacheFactory cacheFactory) { return new DefaultCacheRepository(new DefaultCacheScopeMapping(new File(projectDir, ".gradle"), GradleVersion.current()), cacheFactory); } + + protected DecompressionCache createDecompressionCache(ProjectScopedCache cacheFactory) { + return new DefaultDecompressionCache(cacheFactory); + } } 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 fa9d934ce9de..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 @@ -23,6 +23,7 @@ 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.util.TestUtil; import org.junit.Test; @@ -51,7 +52,7 @@ public class TarFileTreeTest extends AbstractArchiveFileTreeTest { fileSystem(), directoryFileTreeFactory(), fileHasher(), - TestFiles.scopedCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); + TestCaches.decompressionCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); private static Provider asProvider(T object) { return TestUtil.providerFactory().provider(() -> object); @@ -86,7 +87,7 @@ public void readsGzippedTarFile() { rootDir.tgzTo(tgz); MaybeCompressedFileResource resource = new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tgz))); - TarFileTree tree = new TarFileTree(asProvider(tgz), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), TestFiles.scopedCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); + 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")); @@ -101,7 +102,7 @@ public void readsBzippedTarFile() { rootDir.tbzTo(tbz2); MaybeCompressedFileResource resource = new MaybeCompressedFileResource(new LocalResourceAdapter(TestFiles.fileRepository().localResource(tbz2))); - TarFileTree tree = new TarFileTree(asProvider(tbz2), asProvider(resource), fileSystem(), directoryFileTreeFactory(), fileHasher(), TestFiles.scopedCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); + 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")); 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 2f6bf4a53c57..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,7 +18,7 @@ import org.gradle.api.GradleException; import org.gradle.api.InvalidUserDataException; import org.gradle.api.file.EmptyFileVisitor; -import org.gradle.api.internal.file.TestFiles; +import org.gradle.cache.internal.TestCaches; import org.gradle.test.fixtures.file.TestFile; import org.gradle.util.TestUtil; import org.junit.Test; @@ -41,7 +41,7 @@ public class ZipFileTreeTest extends AbstractArchiveFileTreeTest { fileSystem(), directoryFileTreeFactory(), fileHasher(), - TestFiles.scopedCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); + TestCaches.decompressionCache(tempDirProvider.getTestDirectory().createDir("cache-dir"))); @Override protected void archiveFileToRoot(TestFile file) { 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 d6a079fa342b..b7f9ae197e86 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 @@ -15,7 +15,6 @@ */ package org.gradle.api.internal.file; -import org.gradle.api.Action; import org.gradle.api.internal.DocumentationRegistry; import org.gradle.api.internal.cache.StringInterner; import org.gradle.api.internal.file.collections.DefaultDirectoryFileTreeFactory; @@ -29,14 +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.CacheBuilder; -import org.gradle.cache.CacheOpenException; -import org.gradle.cache.CleanupAction; -import org.gradle.cache.LockOptions; -import org.gradle.cache.PersistentCache; -import org.gradle.cache.internal.CacheFactory; -import org.gradle.cache.scopes.ScopedCache; -import org.gradle.internal.Actions; +import org.gradle.cache.internal.TestCaches; import org.gradle.internal.Factory; import org.gradle.internal.concurrent.DefaultExecutorFactory; import org.gradle.internal.file.Deleter; @@ -61,17 +53,12 @@ import org.gradle.process.internal.ExecHandleFactory; import org.gradle.process.internal.JavaExecHandleFactory; import org.gradle.testfixtures.internal.NativeServicesTestFixture; -import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; import org.gradle.util.TestUtil; import javax.annotation.Nullable; import java.io.File; import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import static org.gradle.cache.FileLockManager.LockMode.Exclusive; -import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_INSENSITIVE; import static org.gradle.internal.snapshot.CaseSensitivity.CASE_SENSITIVE; import static org.gradle.util.TestUtil.objectFactory; @@ -187,7 +174,7 @@ public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFi documentationRegistry(), taskDependencyFactory(), providerFactory(), - scopedCache(temporaryFileProvider.newTemporaryDirectory("cache-dir"))); + TestCaches.decompressionCache(temporaryFileProvider.newTemporaryDirectory("cache-dir"))); } public static ApiTextResourceAdapter.Factory textResourceAdapterFactory(@Nullable TemporaryFileProvider temporaryFileProvider) { @@ -293,96 +280,4 @@ public static TemporaryFileProvider tmpDirTemporaryFileProvider(File baseDir) { return new DefaultTemporaryFileProvider(() -> baseDir); } - public static ScopedCache scopedCache(File cacheDir) { - return new TestInMemoryScopedCache(cacheDir); - } - - private static final class TestInMemoryScopedCache implements ScopedCache { - private final File cacheDir; - - private TestInMemoryScopedCache(File cacheDir) { - this.cacheDir = cacheDir; - } - - @Override - public CacheBuilder cache(String key) { - return new TestInMemoryCacheBuilder(baseDirForCache(key)); - } - - @Override - public CacheBuilder crossVersionCache(String key) { - return new TestInMemoryCacheBuilder(baseDirForCrossVersionCache(key)); - } - - @Override - public File getRootDir() { - return cacheDir; - } - - @Override - public File baseDirForCache(String key) { - return new File(cacheDir, "base"); - } - - @Override - public File baseDirForCrossVersionCache(String key) { - return new File(cacheDir, "cross-version-base"); - } - } - - private static final class TestInMemoryCacheBuilder implements CacheBuilder { - private final CacheFactory cacheFactory = new TestInMemoryCacheFactory(); - private final File cacheDir; - private Map properties = Collections.emptyMap(); - private LockTarget lockTarget = LockTarget.DefaultTarget; - private String displayName = "Test In Memory Cache"; - private LockOptions lockOptions = mode(Exclusive); - private Action initializer = Actions.doNothing(); - private CleanupAction cleanup = CleanupAction.NO_OP; - - private TestInMemoryCacheBuilder(File cacheDir) { - this.cacheDir = cacheDir; - } - - @Override - public CacheBuilder withProperties(Map properties) { - this.properties = properties; - return this; - } - - @Override - public CacheBuilder withCrossVersionCache(LockTarget lockTarget) { - this.lockTarget = lockTarget; - return this; - } - - @Override - public CacheBuilder withDisplayName(String displayName) { - this.displayName = displayName; - return this; - } - - @Override - public CacheBuilder withLockOptions(LockOptions lockOptions) { - this.lockOptions = lockOptions; - return this; - } - - @Override - public CacheBuilder withInitializer(Action initializer) { - this.initializer = initializer; - return this; - } - - @Override - public CacheBuilder withCleanup(CleanupAction cleanup) { - this.cleanup = cleanup; - return this; - } - - @Override - public PersistentCache open() throws CacheOpenException { - return cacheFactory.open(cacheDir, displayName, properties, lockTarget, lockOptions, initializer, cleanup); - } - } } 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..f04f3c7e3195 --- /dev/null +++ b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java @@ -0,0 +1,173 @@ +/* + * 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.api.Action; +import org.gradle.cache.CacheBuilder; +import org.gradle.cache.CacheOpenException; +import org.gradle.cache.CleanupAction; +import org.gradle.cache.LockOptions; +import org.gradle.cache.PersistentCache; +import org.gradle.cache.scopes.ScopedCache; +import org.gradle.internal.Actions; +import org.gradle.internal.Factory; +import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; + +import java.io.File; +import java.util.Collections; +import java.util.Map; + +import static org.gradle.cache.FileLockManager.LockMode.Exclusive; +import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; + +/** + * Static util class for obtaining caching-related test doubles for {@link ScopedCache}, {@link PersistentCache} and similar types. + */ +public abstract class TestCaches { + private TestCaches() {} + + public static ScopedCache scopedCache(File cacheDir) { + return new TestInMemoryScopedCache(cacheDir); + } + + public static DecompressionCache decompressionCache(File cacheDir) { + return new TestInMemoryDecompressionCache(cacheDir); + } + + private static final class TestInMemoryDecompressionCache implements DecompressionCache { + private final PersistentCache delegate; + + private TestInMemoryDecompressionCache(File cacheDir) { + TestInMemoryCacheBuilder cacheBuilder = new TestInMemoryCacheBuilder(cacheDir); + delegate = cacheBuilder.open(); + } + + @Override + public T useCache(Factory action) { + return delegate.useCache(action); + } + + @Override + public void useCache(Runnable action) { + delegate.useCache(action); + } + + @Override + public T withFileLock(Factory action) { + return delegate.withFileLock(action); + } + + @Override + public void withFileLock(Runnable action) { + delegate.withFileLock(action); + } + + @Override + public File getBaseDir() { + return delegate.getBaseDir(); + } + } + + private static final class TestInMemoryScopedCache implements ScopedCache { + private final File cacheDir; + + private TestInMemoryScopedCache(File cacheDir) { + this.cacheDir = cacheDir; + } + + @Override + public CacheBuilder cache(String key) { + return new TestInMemoryCacheBuilder(baseDirForCache(key)); + } + + @Override + public CacheBuilder crossVersionCache(String key) { + return new TestInMemoryCacheBuilder(baseDirForCrossVersionCache(key)); + } + + @Override + public File getRootDir() { + return cacheDir; + } + + @Override + public File baseDirForCache(String key) { + return new File(cacheDir, "base"); + } + + @Override + public File baseDirForCrossVersionCache(String key) { + return new File(cacheDir, "cross-version-base"); + } + } + + private static final class TestInMemoryCacheBuilder implements CacheBuilder { + private final CacheFactory cacheFactory = new TestInMemoryCacheFactory(); + private final File cacheDir; + private Map properties = Collections.emptyMap(); + private LockTarget lockTarget = LockTarget.DefaultTarget; + private String displayName = "Test In Memory Cache"; + private LockOptions lockOptions = mode(Exclusive); + private Action initializer = Actions.doNothing(); + private CleanupAction cleanup = CleanupAction.NO_OP; + + private TestInMemoryCacheBuilder(File cacheDir) { + this.cacheDir = cacheDir; + } + + @Override + public CacheBuilder withProperties(Map properties) { + this.properties = properties; + return this; + } + + @Override + public CacheBuilder withCrossVersionCache(LockTarget lockTarget) { + this.lockTarget = lockTarget; + return this; + } + + @Override + public CacheBuilder withDisplayName(String displayName) { + this.displayName = displayName; + return this; + } + + @Override + public CacheBuilder withLockOptions(LockOptions lockOptions) { + this.lockOptions = lockOptions; + return this; + } + + @Override + public CacheBuilder withInitializer(Action initializer) { + this.initializer = initializer; + return this; + } + + @Override + public CacheBuilder withCleanup(CleanupAction cleanup) { + this.cleanup = cleanup; + return this; + } + + @Override + public PersistentCache open() throws CacheOpenException { + return cacheFactory.open(cacheDir, displayName, properties, lockTarget, lockOptions, initializer, cleanup); + } + } +} diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCache.java new file mode 100644 index 000000000000..4c538081c52a --- /dev/null +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCache.java @@ -0,0 +1,32 @@ +/* + * 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.cache.CacheAccess; + +import java.io.File; + +/** + * A cache that can be used to store decompressed data extracted from archive files like zip and tars. + */ +public interface DecompressionCache extends CacheAccess { + /** + * Returns the root directory used by this cache to store decompressed files. + * @return the root directory + */ + File getBaseDir(); +} 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..1775389179f0 --- /dev/null +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java @@ -0,0 +1,85 @@ +/* + * 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.cache.CacheBuilder; +import org.gradle.cache.FileLockManager; +import org.gradle.cache.PersistentCache; +import org.gradle.cache.scopes.ScopedCache; +import org.gradle.internal.Factory; +import org.gradle.internal.concurrent.Stoppable; + +import java.io.Closeable; +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, Stoppable, Closeable { + private static final String EXPANSION_CACHE_KEY = "compressed-file-expansion"; + private static final String EXPANSION_CACHE_NAME = "Compressed Files Expansion Cache"; + + private final PersistentCache cache; + + public DefaultDecompressionCache(ScopedCache cacheFactory) { + this.cache = cacheFactory.cache(EXPANSION_CACHE_KEY) + .withDisplayName(EXPANSION_CACHE_NAME) + .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) + .withLockOptions(mode(FileLockManager.LockMode.Exclusive)) + .open(); + } + + @Override + public T useCache(Factory action) { + return cache.useCache(action); + } + + @Override + public void useCache(Runnable action) { + cache.useCache(action); + } + + @Override + public T withFileLock(Factory action) { + return cache.withFileLock(action); + } + + @Override + public void withFileLock(Runnable action) { + cache.withFileLock(action); + } + + @Override + public File getBaseDir() { + return cache.getBaseDir(); + } + + @Override + public void close() { + cache.close(); + } + + @Override + public void stop() { + close(); + } +} From f9cc27cd9a65d176cbe2aca5d007fa5dcbc0dfb1 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 13:29:04 -0500 Subject: [PATCH 033/120] Inject gradle user home into cache creation service creation methods --- .../service/scopes/WorkerSharedProjectScopeServices.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 3d1b94c083c5..d849acb13295 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 @@ -52,6 +52,7 @@ import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; import org.gradle.cache.scopes.ProjectScopedCache; +import org.gradle.initialization.GradleUserHomeDirProvider; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -157,12 +158,12 @@ DefaultProjectLayout createProjectLayout(FileResolver fileResolver, FileCollecti return new DefaultProjectLayout(projectDir, fileResolver, taskDependencyFactory, patternSetFactory, propertyHost, fileCollectionFactory, filePropertyFactory, fileFactory); } - protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository) { - return new DefaultProjectScopedCache(new File(projectDir, ".gradle"), cacheRepository); + protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository, GradleUserHomeDirProvider gradleUserHomeDirProvider) { + return new DefaultProjectScopedCache(gradleUserHomeDirProvider.getGradleUserHomeDirectory(), cacheRepository); } - protected CacheRepository createCacheRepository(CacheFactory cacheFactory) { - return new DefaultCacheRepository(new DefaultCacheScopeMapping(new File(projectDir, ".gradle"), GradleVersion.current()), cacheFactory); + protected CacheRepository createCacheRepository(CacheFactory cacheFactory, GradleUserHomeDirProvider gradleUserHomeDirProvider) { + return new DefaultCacheRepository(new DefaultCacheScopeMapping(gradleUserHomeDirProvider.getGradleUserHomeDirectory(), GradleVersion.current()), cacheFactory); } protected DecompressionCache createDecompressionCache(ProjectScopedCache cacheFactory) { From 9e8c61f38a496071f09afc4b80c678e9c2293d59 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 13:37:21 -0500 Subject: [PATCH 034/120] Project workers should use the project cache dir --- .../scopes/WorkerSharedProjectScopeServices.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 d849acb13295..49cd2a582f62 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 @@ -52,7 +52,7 @@ import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; import org.gradle.cache.scopes.ProjectScopedCache; -import org.gradle.initialization.GradleUserHomeDirProvider; +import org.gradle.initialization.layout.ProjectCacheDir; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -158,12 +158,12 @@ DefaultProjectLayout createProjectLayout(FileResolver fileResolver, FileCollecti return new DefaultProjectLayout(projectDir, fileResolver, taskDependencyFactory, patternSetFactory, propertyHost, fileCollectionFactory, filePropertyFactory, fileFactory); } - protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository, GradleUserHomeDirProvider gradleUserHomeDirProvider) { - return new DefaultProjectScopedCache(gradleUserHomeDirProvider.getGradleUserHomeDirectory(), cacheRepository); + protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository, ProjectCacheDir projectCacheDir) { + return new DefaultProjectScopedCache(projectCacheDir.getDir(), cacheRepository); } - protected CacheRepository createCacheRepository(CacheFactory cacheFactory, GradleUserHomeDirProvider gradleUserHomeDirProvider) { - return new DefaultCacheRepository(new DefaultCacheScopeMapping(gradleUserHomeDirProvider.getGradleUserHomeDirectory(), GradleVersion.current()), cacheFactory); + protected CacheRepository createCacheRepository(CacheFactory cacheFactory, ProjectCacheDir projectCacheDir) { + return new DefaultCacheRepository(new DefaultCacheScopeMapping(projectCacheDir.getDir(), GradleVersion.current()), cacheFactory); } protected DecompressionCache createDecompressionCache(ProjectScopedCache cacheFactory) { From 8871ba2ba66ed3a2d4cea3eb7d1b1a0c7882d790 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 18:30:45 -0500 Subject: [PATCH 035/120] Calculate location for project-level caches using injected projectLayout --- .../scopes/WorkerSharedProjectScopeServices.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 49cd2a582f62..045bbf60ff7c 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 @@ -52,7 +52,6 @@ import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; import org.gradle.cache.scopes.ProjectScopedCache; -import org.gradle.initialization.layout.ProjectCacheDir; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -158,15 +157,19 @@ DefaultProjectLayout createProjectLayout(FileResolver fileResolver, FileCollecti return new DefaultProjectLayout(projectDir, fileResolver, taskDependencyFactory, patternSetFactory, propertyHost, fileCollectionFactory, filePropertyFactory, fileFactory); } - protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository, ProjectCacheDir projectCacheDir) { - return new DefaultProjectScopedCache(projectCacheDir.getDir(), cacheRepository); + protected CacheRepository createCacheRepository(CacheFactory cacheFactory, DefaultProjectLayout projectLayout) { + return new DefaultCacheRepository(new DefaultCacheScopeMapping(projectLevelCacheDir(projectLayout), GradleVersion.current()), cacheFactory); } - protected CacheRepository createCacheRepository(CacheFactory cacheFactory, ProjectCacheDir projectCacheDir) { - return new DefaultCacheRepository(new DefaultCacheScopeMapping(projectCacheDir.getDir(), GradleVersion.current()), cacheFactory); + protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository, DefaultProjectLayout projectLayout) { + return new DefaultProjectScopedCache(projectLevelCacheDir(projectLayout), cacheRepository); } protected DecompressionCache createDecompressionCache(ProjectScopedCache cacheFactory) { return new DefaultDecompressionCache(cacheFactory); } + + private File projectLevelCacheDir(DefaultProjectLayout projectLayout) { + return projectLayout.getBuildDirectory().file(".cache").get().getAsFile(); + } } From e4c4dc9c21474d417c2241df577b37007d74f374 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 2 Dec 2022 18:54:00 -0500 Subject: [PATCH 036/120] Expand to 3 options for getting a fileOperations instance for testing --- .../bundling/ArchiveIntegrationTest.groovy | 64 +++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) 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 6a90125e1e97..93ae03a2bc18 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 @@ -968,7 +968,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit zip archive differently from two different projects using ArchiveOperations=#useArchiveOps"() { + def "can visit and edit zip archive differently from two different projects using #foSource"() { given: "an archive in the root of a multiproject build" createZip('test.zip') { subdir1 { @@ -983,8 +983,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { and: "where each project edits that same archive differently via a visitor" file('project1/build.gradle') << """ - ${defineUpdateTask('zip', useArchiveOps)} - ${defineVerifyTask('zip', useArchiveOps)} + ${defineUpdateTask('zip', foSource)} + ${defineVerifyTask('zip', foSource)} def theArchive = rootProject.file('test.zip') @@ -1001,8 +1001,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ file('project2/build.gradle') << """ - ${defineUpdateTask('zip', useArchiveOps)} - ${defineVerifyTask('zip', useArchiveOps)} + ${defineUpdateTask('zip', foSource)} + ${defineVerifyTask('zip', foSource)} def theArchive = rootProject.file('test.zip') @@ -1025,11 +1025,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') where: - useArchiveOps << [true, false] + foSource << [FileOperationsSourceType.INJECT_ARCHIVE_OPS, FileOperationsSourceType.PROJECT_FILE_OPS] } @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit tar archive differently from two different projects using ArchiveOperations=#useArchiveOps"() { + def "can visit and edit tar archive differently from two different projects using #foSource"() { given: "an archive in the root of a multiproject build" createTar('test.tar') { subdir1 { @@ -1044,8 +1044,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { and: "where each project edits that same archive differently via a visitor" file('project1/build.gradle') << """ - ${defineUpdateTask('tar', useArchiveOps)} - ${defineVerifyTask('tar', useArchiveOps)} + ${defineUpdateTask('tar', foSource)} + ${defineVerifyTask('tar', foSource)} def theArchive = rootProject.file('test.tar') @@ -1062,8 +1062,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ file('project2/build.gradle') << """ - ${defineUpdateTask('tar', useArchiveOps)} - ${defineVerifyTask('tar', useArchiveOps)} + ${defineUpdateTask('tar', foSource)} + ${defineVerifyTask('tar', foSource)} def theArchive = rootProject.file('test.tar') @@ -1086,7 +1086,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify') where: - useArchiveOps << [true, false] + foSource << [FileOperationsSourceType.INJECT_ARCHIVE_OPS, FileOperationsSourceType.PROJECT_FILE_OPS] } @IgnoreIf({ GradleContextualExecuter.embedded }) @@ -1110,8 +1110,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { and: "a build script which defines two tasks that edit the same tar archive differently, and a verification task that checked that all files contain the same content" buildFile << """ - ${defineUpdateTask('tar')} - ${defineSameContentsTask('tar')} + ${defineUpdateTask('tar', FileOperationsSourceType.INJECT_FILE_OPS)} + ${defineSameContentsTask('tar', FileOperationsSourceType.INJECT_FILE_OPS)} tasks.register('wait1') { doLast { @@ -1187,8 +1187,8 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { and: "a build script which defines two tasks that edit the same zip archive differently, and a verification task that checked that all files contain the same content" buildFile << """ - ${defineUpdateTask('zip')} - ${defineSameContentsTask('zip')} + ${defineUpdateTask('zip', FileOperationsSourceType.INJECT_FILE_OPS)} + ${defineSameContentsTask('zip', FileOperationsSourceType.INJECT_FILE_OPS)} tasks.register('wait1') { doLast { @@ -1283,7 +1283,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ } - private String defineUpdateTask(String archiveType, boolean useArchiveOps = true) { + private String defineUpdateTask(String archiveType, FileOperationsSourceType foSource = FileOperationsSourceType.INJECT_ARCHIVE_OPS) { return """ abstract class UpdateTask extends DefaultTask { @InputFile @@ -1292,11 +1292,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @Input abstract Property getReplacementText() - ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : "private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations"} + ${foSource.declaration} @TaskAction void update() { - FileTree tree = ${useArchiveOps ? "archiveOperations" : "fileOperations"}.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${foSource.usage}.${archiveType}Tree(archive.asFile.get()) tree.visit(new EditingFileVisitor()) } @@ -1313,7 +1313,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ } - private String defineVerifyTask(String archiveType, boolean useArchiveOps) { + private String defineVerifyTask(String archiveType, FileOperationsSourceType foSource = FileOperationsSourceType.INJECT_ARCHIVE_OPS) { return """ abstract class VerifyTask extends DefaultTask { @InputFile @@ -1322,11 +1322,11 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @Input abstract Property getBeginsWith() - ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : "private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations"} + ${foSource.declaration} @TaskAction void verify() { - FileTree tree = ${useArchiveOps ? "archiveOperations" : "fileOperations"}.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${foSource.usage}.${archiveType}Tree(archive.asFile.get()) tree.visit(new VerifyingFileVisitor()) } @@ -1343,17 +1343,17 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { """ } - private String defineSameContentsTask(String archiveType, boolean useArchiveOps = true) { + private String defineSameContentsTask(String archiveType, FileOperationsSourceType foSource = FileOperationsSourceType.INJECT_ARCHIVE_OPS) { return """ abstract class SameContentsTask extends DefaultTask { @InputFile abstract RegularFileProperty getArchive() - ${useArchiveOps ? "@Inject abstract ArchiveOperations getArchiveOperations()" : "private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations"} + ${foSource.declaration} @TaskAction void verify() { - FileTree tree = ${useArchiveOps ? "archiveOperations" : "fileOperations"}.${archiveType}Tree(archive.asFile.get()) + FileTree tree = ${foSource.usage}.${archiveType}Tree(archive.asFile.get()) tree.visit(new VerifyingFileVisitor()) } @@ -1375,4 +1375,18 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } """ } + + private static final enum FileOperationsSourceType { + INJECT_ARCHIVE_OPS("@Inject abstract ArchiveOperations getArchiveOperations()", "archiveOperations"), + INJECT_FILE_OPS("@Inject abstract org.gradle.api.internal.file.FileOperations getFileOperations()", "fileOperations"), + PROJECT_FILE_OPS("private org.gradle.api.internal.file.FileOperations fileOperations = project.fileOperations", "fileOperations") + + private final String declaration + private final String usage + + FileOperationsSourceType(String declaration, String usage) { + this.declaration = declaration + this.usage = usage + } + } } From b2afee6f6a597c78659884f02065391dbc543cb4 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 5 Dec 2022 10:53:36 -0500 Subject: [PATCH 037/120] Add test for multiple process access to .gradle decompression cache via settings file --- .../bundling/ArchiveIntegrationTest.groovy | 172 ++++++++++-------- 1 file changed, 94 insertions(+), 78 deletions(-) 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 93ae03a2bc18..3911474badb9 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 @@ -968,7 +968,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit zip archive differently from two different projects using #foSource"() { + def "can visit and edit zip archive differently from two different projects in a multiproject build using file operations: #foSource"() { given: "an archive in the root of a multiproject build" createZip('test.zip') { subdir1 { @@ -1029,7 +1029,7 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit tar archive differently from two different projects using #foSource"() { + def "can visit and edit tar archive differently from two different projects in a multiproject build using file operations: #foSource"() { given: "an archive in the root of a multiproject build" createTar('test.tar') { subdir1 { @@ -1091,12 +1091,12 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @IgnoreIf({ GradleContextualExecuter.embedded }) @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit tar archive differently from two different tasks in the same project run in two simultaneous processes"() { - given: "a started server which can be used to cause the tasks to begin at approximately the same time" + 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 tar archive in the root with many files with the same content, so that the task won't finish too quickly" - createTar('test.tar') { + 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' @@ -1108,57 +1108,65 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } } - and: "a build script which defines two tasks that edit the same tar archive differently, and a verification task that checked that all files contain the same content" - buildFile << """ - ${defineUpdateTask('tar', FileOperationsSourceType.INJECT_FILE_OPS)} - ${defineSameContentsTask('tar', FileOperationsSourceType.INJECT_FILE_OPS)} + and: "a settings script which edits the zip archive and then verifies that all files contain the same content" + settingsFile << """ + ${server.callFromBuild('wait')} - tasks.register('wait1') { - doLast { - ${server.callFromBuild('wait1')} - } + void update() { + FileTree tree = zipTree(new File(settings.rootDir, 'test.zip')) + tree.visit(new EditingFileVisitor()) } - tasks.register('wait2') { - doLast { - ${server.callFromBuild('wait2')} - } - } + final class EditingFileVisitor implements FileVisitor { + private final int num = new Random().nextInt(100000) - def theArchive = rootProject.file('test.tar') + @Override + void visitDir(FileVisitDetails dirDetails) {} - tasks.register('update1', UpdateTask) { - dependsOn tasks.named('wait1') - archive = theArchive - replacementText = 'modification 1' + @Override + void visitFile(FileVisitDetails fileDetails) { + fileDetails.file.text = fileDetails.file.text.replace('original', Integer.toString(num)) + } } - tasks.register('update2', UpdateTask) { - dependsOn tasks.named('wait2') - archive = theArchive - replacementText = 'modification 2' + void verify() { + FileTree tree = zipTree(new File(settings.rootDir, 'test.zip')) + tree.visit(new VerifyingFileVisitor()) } - tasks.register('verify', SameContentsTask) { - archive = rootProject.file('test.tar') + 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('wait1', 'wait2') - when: "the tasks are run in two processes" - def handle1 = executer.withTasks('update1').start() - def handle2 = executer.withTasks('update2').start() + 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 update tasks were executed" - result1.assertTasksExecuted(':wait1', ':update1') - result2.assertTasksExecuted(':wait2', ':update2') - - and: "verification can complete successfully, ensuring that each task edited the archive atomically" - executer.withTasks('verify').run() + then: "both builds ran, with the settings script editing the archive atomically" + result1.assertTasksExecuted(':tasks') + result2.assertTasksExecuted(':tasks') cleanup: handle1?.abort() @@ -1168,12 +1176,12 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { @IgnoreIf({ GradleContextualExecuter.embedded }) @Issue("https://github.com/gradle/gradle/issues/22685") - def "can visit and edit zip archive differently from two different tasks in the same project run in two simultaneous processes"() { - given: "a started server which can be used to cause the tasks to begin at approximately the same time" + 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 zip archive in the root with many files with the same content, so that the task won't finish too quickly" - createZip('test.zip') { + 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' @@ -1185,57 +1193,65 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { } } - and: "a build script which defines two tasks that edit the same zip archive differently, and a verification task that checked that all files contain the same content" - buildFile << """ - ${defineUpdateTask('zip', FileOperationsSourceType.INJECT_FILE_OPS)} - ${defineSameContentsTask('zip', FileOperationsSourceType.INJECT_FILE_OPS)} + and: "a settings script which edits the tar archive and then verifies that all files contain the same content" + settingsFile << """ + ${server.callFromBuild('wait')} - tasks.register('wait1') { - doLast { - ${server.callFromBuild('wait1')} - } + void update() { + FileTree tree = tarTree(new File(settings.rootDir, 'test.tar')) + tree.visit(new EditingFileVisitor()) } - tasks.register('wait2') { - doLast { - ${server.callFromBuild('wait2')} - } - } + final class EditingFileVisitor implements FileVisitor { + private final int num = new Random().nextInt(100000) - def theArchive = rootProject.file('test.zip') + @Override + void visitDir(FileVisitDetails dirDetails) {} - tasks.register('update1', UpdateTask) { - dependsOn tasks.named('wait1') - archive = theArchive - replacementText = 'modification 1' + @Override + void visitFile(FileVisitDetails fileDetails) { + fileDetails.file.text = fileDetails.file.text.replace('original', Integer.toString(num)) + } } - tasks.register('update2', UpdateTask) { - dependsOn tasks.named('wait2') - archive = theArchive - replacementText = 'modification 2' + void verify() { + FileTree tree = tarTree(new File(settings.rootDir, 'test.tar')) + tree.visit(new VerifyingFileVisitor()) } - tasks.register('verify', SameContentsTask) { - archive = rootProject.file('test.zip') + 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('wait1', 'wait2') - when: "the tasks are run in two processes" - def handle1 = executer.withTasks('update1').start() - def handle2 = executer.withTasks('update2').start() + 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 update tasks were executed" - result1.assertTasksExecuted(':wait1', ':update1') - result2.assertTasksExecuted(':wait2', ':update2') - - and: "verification can complete successfully, ensuring that each task edited the archive atomically" - executer.withTasks('verify').run() + then: "both builds ran, with the settings script editing the archive atomically" + result1.assertTasksExecuted(':tasks') + result2.assertTasksExecuted(':tasks') cleanup: handle1?.abort() From a7e0606df41348d334fe9024e60f2ab7b1162de3 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 5 Dec 2022 10:53:57 -0500 Subject: [PATCH 038/120] Switch to OnDemand caching --- .../groovy/org/gradle/cache/internal/TestCaches.java | 4 ++-- .../org/gradle/cache/internal/DefaultDecompressionCache.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 index f04f3c7e3195..8ccdbaaa6b50 100644 --- a/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java +++ b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java @@ -31,7 +31,7 @@ import java.util.Collections; import java.util.Map; -import static org.gradle.cache.FileLockManager.LockMode.Exclusive; +import static org.gradle.cache.FileLockManager.LockMode.OnDemand; import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode; /** @@ -121,7 +121,7 @@ private static final class TestInMemoryCacheBuilder implements CacheBuilder { private Map properties = Collections.emptyMap(); private LockTarget lockTarget = LockTarget.DefaultTarget; private String displayName = "Test In Memory Cache"; - private LockOptions lockOptions = mode(Exclusive); + private LockOptions lockOptions = mode(OnDemand); private Action initializer = Actions.doNothing(); private CleanupAction cleanup = CleanupAction.NO_OP; diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java index 1775389179f0..7a9f4d464016 100644 --- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java @@ -44,7 +44,7 @@ public DefaultDecompressionCache(ScopedCache cacheFactory) { this.cache = cacheFactory.cache(EXPANSION_CACHE_KEY) .withDisplayName(EXPANSION_CACHE_NAME) .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget) - .withLockOptions(mode(FileLockManager.LockMode.Exclusive)) + .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) .open(); } From 4c46c260ef3510b18c6f981f1f5afb2984e7bf01 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Tue, 6 Dec 2022 08:35:56 -0500 Subject: [PATCH 039/120] Relax assertions to account for new cache directory --- ...vyChangingModuleRemoteResolveIntegrationTest.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy index 8c2e60b964a1..90dc1ee7e995 100644 --- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy +++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyChangingModuleRemoteResolveIntegrationTest.groovy @@ -53,7 +53,7 @@ task retrieve(type: Copy) { run 'retrieve' then: "Version 1.1 jar is downloaded" - file('build').assertHasDescendants('projectA-1.1.jar') + file('build').assertContainsDescendants('projectA-1.1.jar') when: "Module meta-data is changed (new artifact)" module.artifact([name: 'other']) @@ -76,7 +76,7 @@ task retrieve(type: Copy) { run 'retrieve' then: "We get all artifacts, including the new ones" - file('build').assertHasDescendants('projectA-1.1.jar', 'other-1.1.jar', 'projectB-2.0.jar') + file('build').assertContainsDescendants('projectA-1.1.jar', 'other-1.1.jar', 'projectB-2.0.jar') } def "can mark a module as changing after first retrieval"() { @@ -222,7 +222,7 @@ task retrieve(type: Copy) { then: "Version 1.1 jar is downloaded" file('build/reports').maybeDeleteDir() // delete configuration cache report if present - file('build').assertHasDescendants('projectA-1.1.jar') + file('build').assertContainsDescendants('projectA-1.1.jar') def jarFile = file('build/projectA-1.1.jar') jarFile.assertIsCopyOf(module.jarFile) def snapshot = jarFile.snapshot() @@ -237,7 +237,7 @@ task retrieve(type: Copy) { then: "Original module meta-data and artifacts are used" file('build/reports').maybeDeleteDir() // delete configuration cache report if present - file('build').assertHasDescendants('projectA-1.1.jar') + file('build').assertContainsDescendants('projectA-1.1.jar') jarFile.assertHasNotChangedSince(snapshot) when: "Server handles requests" @@ -257,7 +257,7 @@ task retrieve(type: Copy) { then: "We get new artifacts based on the new meta-data" file('build/reports').maybeDeleteDir() // delete configuration cache report if present - file('build').assertHasDescendants('projectA-1.1.jar', 'other-1.1.jar') + file('build').assertContainsDescendants('projectA-1.1.jar', 'other-1.1.jar') jarFile.assertHasChangedSince(snapshot) jarFile.assertIsCopyOf(module.jarFile) } From 8c60c469075cca0869fe87da32f420645b8277ea Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Tue, 6 Dec 2022 09:23:37 -0500 Subject: [PATCH 040/120] Update test to use separate output directory to avoid clashing with new cache dir --- .../GradleImplDepsCompatibilityIntegrationTest.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 From adec1ee9d09e9543dfa0d916a3fd5361627e33e7 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Wed, 7 Dec 2022 16:31:21 -0500 Subject: [PATCH 041/120] Implement Factory services for creating DecompressionCaches --- ...rojectScopedDecompressionCacheFactory.java | 61 +++++++++++++++++++ .../internal/file/DefaultFileOperations.java | 15 ++--- .../buildtree/BuildTreeScopeServices.java | 10 +++ .../WorkerSharedProjectScopeServices.java | 29 ++------- .../gradle/api/internal/file/TestFiles.java | 2 +- .../org/gradle/cache/internal/TestCaches.java | 11 ++++ .../internal/DecompressionCacheFactory.java | 25 ++++++++ 7 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java create mode 100644 subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCacheFactory.java diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java new file mode 100644 index 000000000000..5cb8ecea8c79 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.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.api.file.ProjectLayout; +import org.gradle.cache.CacheRepository; +import org.gradle.cache.internal.CacheFactory; +import org.gradle.cache.internal.DecompressionCache; +import org.gradle.cache.internal.DecompressionCacheFactory; +import org.gradle.cache.internal.DefaultCacheRepository; +import org.gradle.cache.internal.DefaultDecompressionCache; +import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; +import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; +import org.gradle.cache.scopes.ProjectScopedCache; +import org.gradle.util.GradleVersion; + +import java.io.File; + +/** + * A factory that can be used to create {@link DecompressionCache} instances for a particular project. + * + * This type exists so that creation of the caches can be done lazily, avoiding the + * creation of the cache directory (and, by default, the build directory it will be + * a subdirectory of) until it is actually needed. We need a named factory type + * to create a service which can be injected. + */ +public class ProjectScopedDecompressionCacheFactory implements DecompressionCacheFactory { + private final ProjectLayout projectLayout; + private final CacheFactory cacheFactory; + + public ProjectScopedDecompressionCacheFactory(ProjectLayout projectLayout, CacheFactory cacheFactory) { + this.projectLayout = projectLayout; + this.cacheFactory = cacheFactory; + } + + @Override + public DecompressionCache create() { + File cacheDir = projectLevelCacheDir(); + CacheRepository cacheRepository = new DefaultCacheRepository(new DefaultCacheScopeMapping(cacheDir, GradleVersion.current()), cacheFactory); + ProjectScopedCache scopedCache = new DefaultProjectScopedCache(cacheDir, cacheRepository); + return new DefaultDecompressionCache(scopedCache); + } + + private File projectLevelCacheDir() { + return projectLayout.getBuildDirectory().file(".cache").get().getAsFile(); + } +} 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 07f8c99659ac..b4924f6c409d 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 @@ -52,6 +52,7 @@ import org.gradle.api.tasks.WorkResults; import org.gradle.api.tasks.util.PatternSet; import org.gradle.cache.internal.DecompressionCache; +import org.gradle.cache.internal.DecompressionCacheFactory; import org.gradle.internal.Cast; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; @@ -88,7 +89,7 @@ public class DefaultFileOperations implements FileOperations { private final FileCollectionFactory fileCollectionFactory; private final TaskDependencyFactory taskDependencyFactory; private final ProviderFactory providers; - private final DecompressionCache decompressionCache; + private final Factory decompressionCacheFactory; public DefaultFileOperations( FileResolver fileResolver, @@ -106,7 +107,7 @@ public DefaultFileOperations( DocumentationRegistry documentationRegistry, TaskDependencyFactory taskDependencyFactory, ProviderFactory providers, - DecompressionCache decompressionCache + Factory decompressionCacheFactory ) { this.fileCollectionFactory = fileCollectionFactory; this.fileResolver = fileResolver; @@ -132,7 +133,7 @@ public DefaultFileOperations( ); this.fileSystem = fileSystem; this.deleter = deleter; - this.decompressionCache = decompressionCache; + this.decompressionCacheFactory = decompressionCacheFactory; } @Override @@ -182,7 +183,7 @@ public ConfigurableFileTree fileTree(Map args) { @Override public FileTreeInternal zipTree(Object zipPath) { Provider fileProvider = asFileProvider(zipPath); - return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, decompressionCache), taskDependencyFactory, patternSetFactory); + return new FileTreeAdapter(new ZipFileTree(fileProvider, fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory.create()), taskDependencyFactory, patternSetFactory); } @Override @@ -197,7 +198,7 @@ public FileTreeInternal tarTree(Object tarPath) { } }); - TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, decompressionCache); + TarFileTree tarTree = new TarFileTree(fileProvider, resource.map(MaybeCompressedFileResource::new), fileSystem, directoryFileTreeFactory, fileHasher, decompressionCacheFactory.create()); return new FileTreeAdapter(tarTree, taskDependencyFactory, patternSetFactory); } @@ -307,7 +308,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); - DecompressionCache decompressionCache = services.get(DecompressionCache.class); + DecompressionCacheFactory decompressionCacheFactory = services.get(DecompressionCacheFactory.class); DefaultResourceHandler.Factory resourceHandlerFactory = DefaultResourceHandler.Factory.from( fileResolver, @@ -333,6 +334,6 @@ public static DefaultFileOperations createSimple(FileResolver fileResolver, File documentationRegistry, taskDependencyFactory, providers, - decompressionCache); + decompressionCacheFactory); } } 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 3e3d75198b22..ee46af8563b5 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 @@ -22,6 +22,7 @@ import org.gradle.api.logging.configuration.LoggingConfiguration; import org.gradle.api.logging.configuration.ShowStacktrace; import org.gradle.cache.internal.DecompressionCache; +import org.gradle.cache.internal.DecompressionCacheFactory; import org.gradle.cache.internal.DefaultDecompressionCache; import org.gradle.cache.scopes.BuildTreeScopedCache; import org.gradle.execution.DefaultTaskSelector; @@ -102,4 +103,13 @@ protected ExceptionAnalyser createExceptionAnalyser(LoggingConfiguration logging protected DecompressionCache createDecompressionCache(BuildTreeScopedCache cacheFactory) { return new DefaultDecompressionCache(cacheFactory); } + + protected DecompressionCacheFactory createDecompressionCacheFactory(BuildTreeScopedCache cacheFactory) { + return new DecompressionCacheFactory() { + @Override + public DecompressionCache create() { + return new DefaultDecompressionCache(cacheFactory); + } + }; + } } 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 045bbf60ff7c..4f3f0a6e9325 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 @@ -18,7 +18,9 @@ import org.gradle.api.file.ArchiveOperations; import org.gradle.api.file.FileSystemOperations; +import org.gradle.api.file.ProjectLayout; import org.gradle.api.internal.DocumentationRegistry; +import org.gradle.api.internal.cache.ProjectScopedDecompressionCacheFactory; import org.gradle.api.internal.collections.DomainObjectCollectionFactory; import org.gradle.api.internal.file.DefaultArchiveOperations; import org.gradle.api.internal.file.DefaultFileCollectionFactory; @@ -44,14 +46,8 @@ 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.CacheFactory; -import org.gradle.cache.internal.DecompressionCache; -import org.gradle.cache.internal.DefaultCacheRepository; -import org.gradle.cache.internal.DefaultDecompressionCache; -import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; -import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; -import org.gradle.cache.scopes.ProjectScopedCache; +import org.gradle.cache.internal.DecompressionCacheFactory; import org.gradle.internal.Factory; import org.gradle.internal.file.Deleter; import org.gradle.internal.hash.FileHasher; @@ -64,7 +60,6 @@ import org.gradle.process.ExecOperations; import org.gradle.process.internal.DefaultExecOperations; import org.gradle.process.internal.ExecFactory; -import org.gradle.util.GradleVersion; import java.io.File; @@ -104,7 +99,7 @@ protected DefaultFileOperations createFileOperations( DocumentationRegistry documentationRegistry, ProviderFactory providers, TaskDependencyFactory taskDependencyFactory, - DecompressionCache decompressionCache + DecompressionCacheFactory decompressionCache ) { return new DefaultFileOperations( fileResolver, @@ -157,19 +152,7 @@ DefaultProjectLayout createProjectLayout(FileResolver fileResolver, FileCollecti return new DefaultProjectLayout(projectDir, fileResolver, taskDependencyFactory, patternSetFactory, propertyHost, fileCollectionFactory, filePropertyFactory, fileFactory); } - protected CacheRepository createCacheRepository(CacheFactory cacheFactory, DefaultProjectLayout projectLayout) { - return new DefaultCacheRepository(new DefaultCacheScopeMapping(projectLevelCacheDir(projectLayout), GradleVersion.current()), cacheFactory); - } - - protected ProjectScopedCache createProjectScopedCache(CacheRepository cacheRepository, DefaultProjectLayout projectLayout) { - return new DefaultProjectScopedCache(projectLevelCacheDir(projectLayout), cacheRepository); - } - - protected DecompressionCache createDecompressionCache(ProjectScopedCache cacheFactory) { - return new DefaultDecompressionCache(cacheFactory); - } - - private File projectLevelCacheDir(DefaultProjectLayout projectLayout) { - return projectLayout.getBuildDirectory().file(".cache").get().getAsFile(); + protected DecompressionCacheFactory createDecompressionCacheFactory(ProjectLayout projectLayout, CacheFactory cacheFactory) { + return new ProjectScopedDecompressionCacheFactory(projectLayout, cacheFactory); } } 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 b7f9ae197e86..9a90b1979f7a 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 @@ -174,7 +174,7 @@ public static FileOperations fileOperations(File basedDir, @Nullable TemporaryFi documentationRegistry(), taskDependencyFactory(), providerFactory(), - TestCaches.decompressionCache(temporaryFileProvider.newTemporaryDirectory("cache-dir"))); + TestCaches.decompressionCacheFactory(temporaryFileProvider.newTemporaryDirectory("cache-dir"))); } public static ApiTextResourceAdapter.Factory textResourceAdapterFactory(@Nullable TemporaryFileProvider temporaryFileProvider) { 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 index 8ccdbaaa6b50..ed1cd89e73a8 100644 --- a/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java +++ b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java @@ -27,6 +27,7 @@ import org.gradle.internal.Factory; import org.gradle.testfixtures.internal.TestInMemoryCacheFactory; +import javax.annotation.Nullable; import java.io.File; import java.util.Collections; import java.util.Map; @@ -48,6 +49,16 @@ public static DecompressionCache decompressionCache(File cacheDir) { return new TestInMemoryDecompressionCache(cacheDir); } + public static DecompressionCacheFactory decompressionCacheFactory(File cacheDir) { + return new DecompressionCacheFactory() { + @Nullable + @Override + public DecompressionCache create() { + return decompressionCache(cacheDir); + } + }; + } + private static final class TestInMemoryDecompressionCache implements DecompressionCache { private final PersistentCache delegate; diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCacheFactory.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCacheFactory.java new file mode 100644 index 000000000000..dc1c932d7df7 --- /dev/null +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DecompressionCacheFactory.java @@ -0,0 +1,25 @@ +/* + * 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.internal.Factory; + +/** + * A factory for creating {@link DecompressionCache} instances. + */ +public interface DecompressionCacheFactory extends Factory { +} From b3d6baf4e7ecffa2aaf869cb72b0e97d135aea0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Don=C3=A1t=20Csik=C3=B3s?= Date: Thu, 8 Dec 2022 14:35:44 +0100 Subject: [PATCH 042/120] Fix 'structuring-software-projects' sample Importing the sample to IDEA is currently fails with a following error message: ``` Unresolved module dependency (...) Neither the source set nor the artifact property was populated by the Android Gradle plugin. ``` This issue seems to be the root cause: https://issuetracker.google.com/issues/247066500 Updating AGP to 7.3.1 locally fixes the issue. --- .../kotlin/platforms/plugins-platform/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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") From fcb09bdfb2fd11e779b5520777efd9fea9927df5 Mon Sep 17 00:00:00 2001 From: Reinhold Degenfellner Date: Fri, 9 Dec 2022 12:02:10 +0100 Subject: [PATCH 043/120] Change deprecation of custom layout to be removed in 9.0 instead of 8.0 Fixes #23027 Signed-off-by: Reinhold Degenfellner --- .../BuildInitPluginIntegrationTest.groovy | 2 +- ...eBuildCommandLineArgsIntegrationTest.groovy | 4 ++-- ...tingsScriptVisibilityIntegrationTest.groovy | 2 +- .../GradleBuildTaskIntegrationTest.groovy | 2 +- ...ettingsBuildOperationIntegrationTest.groovy | 2 +- ...ructureBuildOperationIntegrationTest.groovy | 2 +- .../BuildSrcLocationIntegrationTest.groovy | 4 ++-- ...DefaultBuildLifecycleControllerFactory.java | 18 ++++++++++-------- .../BuildScanAutoApplyIntegrationTest.groovy | 2 +- .../ProjectLoadingIntegrationTest.java | 4 ++-- ...NativeLanguageSamplesIntegrationTest.groovy | 2 +- ...GradleBuildGroupedTaskFunctionalTest.groovy | 2 +- 12 files changed, 24 insertions(+), 22 deletions(-) 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/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/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/tasks/GradleBuildTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/GradleBuildTaskIntegrationTest.groovy index f3bad0c5b9b5..cd0e105ff6dd 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' 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/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/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/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/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..b85698805091 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 @@ -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: 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/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/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: From a55e60cd1e456b35be584ffce4317240d4084603 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:02:33 -0500 Subject: [PATCH 044/120] Fix NPE in tests when creating cache dir --- .../file/DefaultFileOperationsTest.groovy | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 ed2ac6e2cd1d..9e7a4ad3341e 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,9 +24,10 @@ 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.initialization.GradleUserHomeDirProvider +import org.gradle.cache.internal.TestCaches import org.gradle.internal.hash.FileHasher import org.gradle.internal.hash.StreamHasher import org.gradle.internal.reflect.Instantiator @@ -36,13 +37,20 @@ 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() @@ -50,8 +58,6 @@ class DefaultFileOperationsTest extends Specification { 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( @@ -71,8 +77,7 @@ class DefaultFileOperationsTest extends Specification { TestFiles.documentationRegistry(), TestFiles.taskDependencyFactory(), TestUtil.providerFactory(), - TestFiles.cacheFactory(), - (GradleUserHomeDirProvider)(() -> temporaryFileProvider.newTemporaryDirectory("user-home")) + TestCaches.decompressionCacheFactory(temporaryFileProvider.newTemporaryDirectory("cache")) ) } From f62f359edc029b404a627adb9185597105431c8c Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:11:02 -0500 Subject: [PATCH 045/120] Fix NPE in tests when creating cache dir --- .../transaction/CompileTransactionTest.groovy | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy index 03368ddf2eeb..6a6dfb39a7df 100644 --- a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy +++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy @@ -17,6 +17,8 @@ package org.gradle.api.internal.tasks.compile.incremental.transaction import org.gradle.api.internal.file.TestFiles +import org.gradle.api.internal.file.temp.DefaultTemporaryFileProvider +import org.gradle.api.internal.file.temp.TemporaryFileProvider import org.gradle.api.internal.tasks.compile.CompilationFailedException import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec import org.gradle.api.internal.tasks.compile.JavaCompileSpec @@ -43,6 +45,7 @@ class CompileTransactionTest extends Specification { File transactionDir File stashDir JavaCompileSpec spec + TemporaryFileProvider temporaryFileProvider = new DefaultTemporaryFileProvider(() -> new File(temporaryFolder, "cache")); def setup() { transactionDir = new File(temporaryFolder, "compileTransaction") @@ -56,15 +59,15 @@ class CompileTransactionTest extends Specification { } CompileTransaction newCompileTransaction() { - return new CompileTransaction(spec, new PatternSet(), Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder), TestFiles.deleter()) + return new CompileTransaction(spec, new PatternSet(), Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder, temporaryFileProvider), TestFiles.deleter()) } CompileTransaction newCompileTransaction(PatternSet classesToDelete) { - return new CompileTransaction(spec, classesToDelete, Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder), TestFiles.deleter()) + return new CompileTransaction(spec, classesToDelete, Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder, temporaryFileProvider), TestFiles.deleter()) } CompileTransaction newCompileTransaction(PatternSet classesToDelete, Map resourcesToDelete) { - return new CompileTransaction(spec, classesToDelete, resourcesToDelete, TestFiles.fileOperations(temporaryFolder), TestFiles.deleter()) + return new CompileTransaction(spec, classesToDelete, resourcesToDelete, TestFiles.fileOperations(temporaryFolder, temporaryFileProvider), TestFiles.deleter()) } def "transaction base directory is cleared before execution"() { From 637200877afe9594462f21b384216130a814e8c6 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:14:07 -0500 Subject: [PATCH 046/120] Fix NPE in tests when creating cache dir --- .../org/gradle/api/internal/project/DefaultProjectSpec.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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() From 41835f5781e8d68d3b165034e06794af7efae673 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:16:29 -0500 Subject: [PATCH 047/120] Maintain reference to decompression caches created by factory to close them upon closing factory This should fix issues with files remaining in use and unable to be deleted in windows now that the cache itself is not a service. The service lifecycle will affect the factory, and the factory will shut down the caches it has created. --- ...rojectScopedDecompressionCacheFactory.java | 29 +++++++++++++++++-- .../internal/DefaultDecompressionCache.java | 8 +---- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java index 5cb8ecea8c79..99047c8dcf94 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java @@ -26,9 +26,14 @@ import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; import org.gradle.cache.scopes.ProjectScopedCache; +import org.gradle.internal.concurrent.Stoppable; import org.gradle.util.GradleVersion; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; /** * A factory that can be used to create {@link DecompressionCache} instances for a particular project. @@ -38,9 +43,10 @@ * a subdirectory of) until it is actually needed. We need a named factory type * to create a service which can be injected. */ -public class ProjectScopedDecompressionCacheFactory implements DecompressionCacheFactory { +public class ProjectScopedDecompressionCacheFactory implements DecompressionCacheFactory, Stoppable, Closeable { private final ProjectLayout projectLayout; private final CacheFactory cacheFactory; + private final Set createdCaches = new HashSet<>(); public ProjectScopedDecompressionCacheFactory(ProjectLayout projectLayout, CacheFactory cacheFactory) { this.projectLayout = projectLayout; @@ -48,14 +54,31 @@ public ProjectScopedDecompressionCacheFactory(ProjectLayout projectLayout, Cache } @Override - public DecompressionCache create() { + public DefaultDecompressionCache create() { File cacheDir = projectLevelCacheDir(); CacheRepository cacheRepository = new DefaultCacheRepository(new DefaultCacheScopeMapping(cacheDir, GradleVersion.current()), cacheFactory); ProjectScopedCache scopedCache = new DefaultProjectScopedCache(cacheDir, cacheRepository); - return new DefaultDecompressionCache(scopedCache); + + DefaultDecompressionCache cache = new DefaultDecompressionCache(scopedCache); + createdCaches.add(cache); + return cache; } private File projectLevelCacheDir() { return projectLayout.getBuildDirectory().file(".cache").get().getAsFile(); } + + @Override + public void close() throws IOException { + createdCaches.forEach(DefaultDecompressionCache::close); + } + + @Override + public void stop() { + try { + close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java index 7a9f4d464016..e01b796b3cb9 100644 --- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java +++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java @@ -21,7 +21,6 @@ import org.gradle.cache.PersistentCache; import org.gradle.cache.scopes.ScopedCache; import org.gradle.internal.Factory; -import org.gradle.internal.concurrent.Stoppable; import java.io.Closeable; import java.io.File; @@ -34,7 +33,7 @@ * 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, Stoppable, Closeable { +public class DefaultDecompressionCache implements DecompressionCache, Closeable { private static final String EXPANSION_CACHE_KEY = "compressed-file-expansion"; private static final String EXPANSION_CACHE_NAME = "Compressed Files Expansion Cache"; @@ -77,9 +76,4 @@ public File getBaseDir() { public void close() { cache.close(); } - - @Override - public void stop() { - close(); - } } From ce0af9ec6655f43329514a55df323f9ce179d9a9 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:21:59 -0500 Subject: [PATCH 048/120] Make 1-arg fileOperations supply a default tmp dir provider --- .../groovy/org/gradle/api/internal/file/TestFiles.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 9a90b1979f7a..9c30334e4d47 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 @@ -143,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(); From 1239f8b84f79f98623e1edecd0810faef224c954 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:22:20 -0500 Subject: [PATCH 049/120] Revert "Fix NPE in tests when creating cache dir" This reverts commit a55e60cd1e456b35be584ffce4317240d4084603. --- .../file/DefaultFileOperationsTest.groovy | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 9e7a4ad3341e..ed2ac6e2cd1d 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,9 @@ 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.initialization.GradleUserHomeDirProvider import org.gradle.internal.hash.FileHasher import org.gradle.internal.hash.StreamHasher import org.gradle.internal.reflect.Instantiator @@ -37,20 +36,13 @@ 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 = new DefaultTemporaryFileProvider(() -> cacheDir); + private final TemporaryFileProvider temporaryFileProvider = Mock() private final Instantiator instantiator = TestUtil.instantiatorFactory().decorateLenient() private final DefaultDirectoryFileTreeFactory directoryFileTreeFactory = Mock() private final StreamHasher streamHasher = Mock() @@ -58,6 +50,8 @@ class DefaultFileOperationsTest extends Specification { 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( @@ -77,7 +71,8 @@ class DefaultFileOperationsTest extends Specification { TestFiles.documentationRegistry(), TestFiles.taskDependencyFactory(), TestUtil.providerFactory(), - TestCaches.decompressionCacheFactory(temporaryFileProvider.newTemporaryDirectory("cache")) + TestFiles.cacheFactory(), + (GradleUserHomeDirProvider)(() -> temporaryFileProvider.newTemporaryDirectory("user-home")) ) } From dd661681633557a907de4a44ed765ed2d7788818 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:22:33 -0500 Subject: [PATCH 050/120] Revert "Fix NPE in tests when creating cache dir" This reverts commit f62f359edc029b404a627adb9185597105431c8c. --- .../transaction/CompileTransactionTest.groovy | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy index 6a6dfb39a7df..03368ddf2eeb 100644 --- a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy +++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/incremental/transaction/CompileTransactionTest.groovy @@ -17,8 +17,6 @@ package org.gradle.api.internal.tasks.compile.incremental.transaction import org.gradle.api.internal.file.TestFiles -import org.gradle.api.internal.file.temp.DefaultTemporaryFileProvider -import org.gradle.api.internal.file.temp.TemporaryFileProvider import org.gradle.api.internal.tasks.compile.CompilationFailedException import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec import org.gradle.api.internal.tasks.compile.JavaCompileSpec @@ -45,7 +43,6 @@ class CompileTransactionTest extends Specification { File transactionDir File stashDir JavaCompileSpec spec - TemporaryFileProvider temporaryFileProvider = new DefaultTemporaryFileProvider(() -> new File(temporaryFolder, "cache")); def setup() { transactionDir = new File(temporaryFolder, "compileTransaction") @@ -59,15 +56,15 @@ class CompileTransactionTest extends Specification { } CompileTransaction newCompileTransaction() { - return new CompileTransaction(spec, new PatternSet(), Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder, temporaryFileProvider), TestFiles.deleter()) + return new CompileTransaction(spec, new PatternSet(), Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder), TestFiles.deleter()) } CompileTransaction newCompileTransaction(PatternSet classesToDelete) { - return new CompileTransaction(spec, classesToDelete, Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder, temporaryFileProvider), TestFiles.deleter()) + return new CompileTransaction(spec, classesToDelete, Collections.emptyMap(), TestFiles.fileOperations(temporaryFolder), TestFiles.deleter()) } CompileTransaction newCompileTransaction(PatternSet classesToDelete, Map resourcesToDelete) { - return new CompileTransaction(spec, classesToDelete, resourcesToDelete, TestFiles.fileOperations(temporaryFolder, temporaryFileProvider), TestFiles.deleter()) + return new CompileTransaction(spec, classesToDelete, resourcesToDelete, TestFiles.fileOperations(temporaryFolder), TestFiles.deleter()) } def "transaction base directory is cleared before execution"() { From 0beee721b6d668efb683524be014df478380b051 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Fri, 9 Dec 2022 10:25:26 -0500 Subject: [PATCH 051/120] Revert "Revert "Fix NPE in tests when creating cache dir"" This reverts commit 1239f8b84f79f98623e1edecd0810faef224c954. --- .../file/DefaultFileOperationsTest.groovy | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 ed2ac6e2cd1d..9e7a4ad3341e 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,9 +24,10 @@ 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.initialization.GradleUserHomeDirProvider +import org.gradle.cache.internal.TestCaches import org.gradle.internal.hash.FileHasher import org.gradle.internal.hash.StreamHasher import org.gradle.internal.reflect.Instantiator @@ -36,13 +37,20 @@ 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() @@ -50,8 +58,6 @@ class DefaultFileOperationsTest extends Specification { 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( @@ -71,8 +77,7 @@ class DefaultFileOperationsTest extends Specification { TestFiles.documentationRegistry(), TestFiles.taskDependencyFactory(), TestUtil.providerFactory(), - TestFiles.cacheFactory(), - (GradleUserHomeDirProvider)(() -> temporaryFileProvider.newTemporaryDirectory("user-home")) + TestCaches.decompressionCacheFactory(temporaryFileProvider.newTemporaryDirectory("cache")) ) } From a4a883616fd7e9130b5c94e6482a43e59ee601b9 Mon Sep 17 00:00:00 2001 From: Mikhail Lopatkin Date: Fri, 9 Dec 2022 20:26:36 +0100 Subject: [PATCH 052/120] Add snippets for BuildService implementing OperationCompleteListener --- .../extending-gradle/build_services.adoc | 28 +++++++++++++++++-- .../buildServiceListener/groovy/build.gradle | 9 ++++++ .../groovy/buildSrc/build.gradle | 16 +++++++++++ .../src/main/java/TaskEventsPlugin.java | 20 +++++++++++++ .../src/main/java/TaskEventsService.java | 22 +++++++++++++++ .../groovy/settings.gradle | 1 + .../tests/registerListener.out | 2 ++ .../tests/registerListener.sample.conf | 3 ++ 8 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/build.gradle create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/build.gradle create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsPlugin.java create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/buildSrc/src/main/java/TaskEventsService.java create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/groovy/settings.gradle create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.out create mode 100644 subprojects/docs/src/snippets/plugins/buildServiceListener/tests/registerListener.sample.conf 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/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 From 699fcc3dc13e78845c9850b6e43f611559a60bc8 Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Thu, 8 Dec 2022 09:47:47 +0100 Subject: [PATCH 053/120] Smoke test Kotlin 1.8.0-RC Signed-off-by: Paul Merlin --- subprojects/docs/src/docs/userguide/compatibility.adoc | 2 +- .../fixtures/versions/KotlinGradlePluginVersions.groovy | 3 ++- .../groovy/org/gradle/smoketests/AbstractSmokeTest.groovy | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/subprojects/docs/src/docs/userguide/compatibility.adoc b/subprojects/docs/src/docs/userguide/compatibility.adoc index a888eecd76d8..7a3e9749f12f 100644 --- a/subprojects/docs/src/docs/userguide/compatibility.adoc +++ b/subprojects/docs/src/docs/userguide/compatibility.adoc @@ -46,7 +46,7 @@ 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. 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/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy index 72195a059806..fe8c3986d7dc 100644 --- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy +++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy @@ -170,7 +170,7 @@ abstract class AbstractSmokeTest extends Specification { // https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.allopen // https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.spring - static kotlinPlugins = Versions.of("1.6.10", "1.6.21", "1.7.0", "1.7.10", "1.7.22") + static kotlinPlugins = Versions.of("1.6.10", "1.6.21", "1.7.0", "1.7.10", "1.7.22", "1.8.0-RC") // https://plugins.gradle.org/plugin/com.moowork.grunt // https://plugins.gradle.org/plugin/com.moowork.gulp From 9d62396de16f0b0940e7f9b3f5d47c36e3ea36ac Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Thu, 8 Dec 2022 16:07:06 +0100 Subject: [PATCH 054/120] Remove unnecessary condition Signed-off-by: Paul Merlin --- .../groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy index 963f6ea23847..a83789bf81ec 100644 --- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy +++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy @@ -323,7 +323,7 @@ class KotlinPluginSmokeTest extends AbstractPluginValidatingSmokeTest implements """ } alwaysPasses() - if (testedPluginId == 'org.jetbrains.kotlin.js' && version != '1.3.72') { + if (testedPluginId == 'org.jetbrains.kotlin.js') { buildFile << """ kotlin { js { browser() } } """ From 0e6c81ce4972bc110441ecc5507dfa587a3f503f Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Thu, 8 Dec 2022 16:09:28 +0100 Subject: [PATCH 055/120] Let KotlinPluginSmokeTest use js(IR) for static validation Signed-off-by: Paul Merlin --- .../groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy index a83789bf81ec..f9b22f2bf6f2 100644 --- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy +++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy @@ -325,14 +325,14 @@ class KotlinPluginSmokeTest extends AbstractPluginValidatingSmokeTest implements alwaysPasses() if (testedPluginId == 'org.jetbrains.kotlin.js') { buildFile << """ - kotlin { js { browser() } } + kotlin { js(IR) { browser() } } """ } if (testedPluginId == 'org.jetbrains.kotlin.multiplatform') { buildFile << """ kotlin { jvm() - js { browser() } + js(IR) { browser() } } """ } From 4c30af9157e6544ec6d70abda5ed99b23159946d Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Thu, 8 Dec 2022 16:23:14 +0100 Subject: [PATCH 056/120] Fix Kotlin/Android smoke tests to work with 1.6 -> 1.8 Signed-off-by: Paul Merlin --- .../android-kotlin-example-kotlin-dsl/app/build.gradle.kts | 4 +++- .../android-kotlin-example-kotlin-dsl/build.gradle.kts | 1 - .../gradle/smoketests/android-kotlin-example/app/build.gradle | 4 +++- .../org/gradle/smoketests/android-kotlin-example/build.gradle | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts index 656cb03ca55c..cf3260879492 100644 --- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts +++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts @@ -17,7 +17,6 @@ plugins { id("com.android.application") id("kotlin-android") - id("kotlin-android-extensions") id("jacoco") } @@ -33,6 +32,9 @@ android { versionName = "1.0" testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" } + buildFeatures { + viewBinding = true + } buildTypes { getByName("release") { isMinifyEnabled = false diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/build.gradle.kts b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/build.gradle.kts index cd90da283ee8..4aea31b7aa12 100644 --- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/build.gradle.kts +++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/build.gradle.kts @@ -18,7 +18,6 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle:$androidPluginVersion") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - classpath("org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion") } repositories { mavenCentral() diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/app/build.gradle b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/app/build.gradle index 9fc1e673fc11..a91598fe064e 100644 --- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/app/build.gradle +++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/app/build.gradle @@ -1,7 +1,6 @@ plugins { id 'com.android.application' id 'kotlin-android' - id 'kotlin-android-extensions' id 'jacoco' } @@ -17,6 +16,9 @@ android { versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + buildFeatures { + viewBinding true + } buildTypes { release { minifyEnabled false diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/build.gradle b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/build.gradle index e1f892c8e9d8..2a2ababaf2da 100644 --- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/build.gradle +++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example/build.gradle @@ -6,7 +6,6 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:$androidPluginVersion' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion' - classpath 'org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion' } } From 15cb9a02ce4a89470f225b49c8cff0191ac61214 Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Fri, 9 Dec 2022 12:33:37 +0100 Subject: [PATCH 057/120] One sentence per line in compatibility.adoc Signed-off-by: Paul Merlin --- subprojects/docs/src/docs/userguide/compatibility.adoc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/subprojects/docs/src/docs/userguide/compatibility.adoc b/subprojects/docs/src/docs/userguide/compatibility.adoc index 7a3e9749f12f..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.8.0. Beta and RC versions may or may not work. +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. From 016cc616f024a448afa84c5fc833cb60cb025949 Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Fri, 9 Dec 2022 13:30:15 +0100 Subject: [PATCH 058/120] Fix jvm toolchains test for Kotlin 1.8 Signed-off-by: Paul Merlin --- ...JavaToolchainBuildOperationsFixture.groovy | 21 +++++++++- ...chainBuildOperationsIntegrationTest.groovy | 42 +++++++++++-------- 2 files changed, 44 insertions(+), 19 deletions(-) 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/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..a61b71c07ff8 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 isKotlin_1_6 = kotlinVersionNumber.baseVersion < VersionNumber.parse("1.7.0") + def isKotlin_1_8 = kotlinVersionNumber.baseVersion >= VersionNumber.parse("1.8.0") - if (VersionNumber.parse(kotlinPluginVersion) <= VersionNumber.parse("1.6.21")) { + when: + if (isKotlin_1_6) { 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 (isKotlin_1_8) { + // 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 (isKotlin_1_6 && 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) } From 49e203f9a4c88e80c900b4fb301b42e36a969b69 Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Fri, 9 Dec 2022 13:58:52 +0100 Subject: [PATCH 059/120] Make codenarc happy Signed-off-by: Paul Merlin --- .../JavaToolchainBuildOperationsIntegrationTest.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 a61b71c07ff8..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 @@ -427,11 +427,11 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe and: def kotlinVersionNumber = VersionNumber.parse(kotlinPluginVersion) - def isKotlin_1_6 = kotlinVersionNumber.baseVersion < VersionNumber.parse("1.7.0") - def isKotlin_1_8 = kotlinVersionNumber.baseVersion >= VersionNumber.parse("1.8.0") + def isKotlin1dot6 = kotlinVersionNumber.baseVersion < VersionNumber.parse("1.7.0") + def isKotlin1dot8 = kotlinVersionNumber.baseVersion >= VersionNumber.parse("1.8.0") when: - if (isKotlin_1_6) { + if (isKotlin1dot6) { executer.expectDocumentedDeprecationWarning( "The AbstractCompile.destinationDir property has been deprecated. " + "This is scheduled to be removed in Gradle 9.0. " + @@ -445,7 +445,7 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe then: executedAndNotSkipped(":compileKotlin", ":test") println(eventsOnCompile) - if (isKotlin_1_8) { + if (isKotlin1dot8) { // Kotlin 1.8 uses both launcher and compiler assertToolchainUsages(eventsOnCompile, jdkMetadata, "JavaLauncher", "JavaCompiler") } else { @@ -462,7 +462,7 @@ class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpe eventsOnTest = toolchainEvents(":test") then: - if (isKotlin_1_6 && 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 { From 0e573b2b0aaf3a139a72cce2b13845cb11bef114 Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Sat, 10 Dec 2022 10:07:22 +0100 Subject: [PATCH 060/120] Remove now unused expectations in AndroidSantaTrackerCachingSmokeTest Signed-off-by: Paul Merlin --- ...AndroidSantaTrackerCachingSmokeTest.groovy | 3154 +---------------- 1 file changed, 1 insertion(+), 3153 deletions(-) diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy index a61dcacb5491..0015145c0109 100644 --- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy +++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy @@ -60,17 +60,7 @@ class AndroidSantaTrackerCachingSmokeTest extends AbstractAndroidSantaTrackerSmo assertConfigurationCacheStateStored() and: - def expectedResults = agpVersion.startsWith('4.1') - ? AndroidPluginExpectations4.EXPECTED_RESULTS_4_1 - : agpVersion.startsWith('4.2') - ? AndroidPluginExpectations4.EXPECTED_RESULTS_4_2 - : agpVersion.startsWith('7.0') - ? AndroidPluginExpectations70.EXPECTED_RESULTS_7_0 - : agpVersion.startsWith('7.1') - ? AndroidPluginExpectations71.EXPECTED_RESULTS_7_1 - : agpVersion.startsWith('7.2') - ? AndroidPluginExpectations7.EXPECTED_RESULTS_7_2 - : agpVersion.startsWith('7.3') + def expectedResults = agpVersion.startsWith('7.3') ? AndroidPluginExpectations7.EXPECTED_RESULTS_7_3 : agpVersion.startsWith('7.4') ? AndroidPluginExpectations7.EXPECTED_RESULTS_7_4 @@ -1988,3146 +1978,4 @@ class AndroidPluginExpectations7 { ':wearable:writeDebugAppMetadata': SUCCESS, ':wearable:writeDebugSigningConfigVersions': SUCCESS, ] - - static final EXPECTED_RESULTS_7_2 = [ - ':cityquiz:assembleDebug': SUCCESS, - ':cityquiz:checkDebugAarMetadata': SUCCESS, - ':cityquiz:checkDebugDuplicateClasses': SUCCESS, - ':cityquiz:compileDebugAidl': NO_SOURCE, - ':cityquiz:compileDebugJavaWithJavac': FROM_CACHE, - ':cityquiz:compileDebugKotlin': FROM_CACHE, - ':cityquiz:compileDebugRenderscript': NO_SOURCE, - ':cityquiz:compileDebugShaders': NO_SOURCE, - ':cityquiz:compressDebugAssets': FROM_CACHE, - ':cityquiz:createDebugApkListingFileRedirect': SUCCESS, - ':cityquiz:createDebugCompatibleScreenManifests': SUCCESS, - ':cityquiz:desugarDebugFileDependencies': FROM_CACHE, - ':cityquiz:dexBuilderDebug': FROM_CACHE, - ':cityquiz:extractDeepLinksDebug': FROM_CACHE, - ':cityquiz:featureDebugWriter': SUCCESS, - ':cityquiz:generateDebugAssets': UP_TO_DATE, - ':cityquiz:generateDebugBuildConfig': FROM_CACHE, - ':cityquiz:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':cityquiz:generateDebugResValues': FROM_CACHE, - ':cityquiz:generateDebugResources': UP_TO_DATE, - ':cityquiz:javaPreCompileDebug': FROM_CACHE, - ':cityquiz:mapDebugSourceSetPaths': SUCCESS, - ':cityquiz:mergeDebugAssets': FROM_CACHE, - ':cityquiz:mergeDebugJavaResource': SUCCESS, - ':cityquiz:mergeDebugJniLibFolders': FROM_CACHE, - ':cityquiz:mergeDebugNativeLibs': NO_SOURCE, - ':cityquiz:mergeDebugResources': SUCCESS, - ':cityquiz:mergeDebugShaders': FROM_CACHE, - ':cityquiz:mergeExtDexDebug': FROM_CACHE, - ':cityquiz:mergeLibDexDebug': FROM_CACHE, - ':cityquiz:mergeProjectDexDebug': FROM_CACHE, - ':cityquiz:packageDebug': SUCCESS, - ':cityquiz:preBuild': UP_TO_DATE, - ':cityquiz:preDebugBuild': UP_TO_DATE, - ':cityquiz:processApplicationManifestDebugForBundle': SUCCESS, - ':cityquiz:processDebugJavaRes': NO_SOURCE, - ':cityquiz:processDebugMainManifest': FROM_CACHE, - ':cityquiz:processDebugManifest': FROM_CACHE, - ':cityquiz:processDebugManifestForPackage': FROM_CACHE, - ':cityquiz:processDebugResources': FROM_CACHE, - ':cityquiz:processManifestDebugForFeature': FROM_CACHE, - ':cityquiz:stripDebugDebugSymbols': NO_SOURCE, - ':common:assembleDebug': SUCCESS, - ':common:bundleDebugAar': SUCCESS, - ':common:bundleLibCompileToJarDebug': SUCCESS, - ':common:bundleLibResDebug': SUCCESS, - ':common:bundleLibRuntimeToJarDebug': SUCCESS, - ':common:compileDebugAidl': NO_SOURCE, - ':common:compileDebugJavaWithJavac': FROM_CACHE, - ':common:compileDebugKotlin': FROM_CACHE, - ':common:compileDebugLibraryResources': FROM_CACHE, - ':common:compileDebugRenderscript': NO_SOURCE, - ':common:compileDebugShaders': NO_SOURCE, - ':common:copyDebugJniLibsProjectAndLocalJars': SUCCESS, - ':common:copyDebugJniLibsProjectOnly': SUCCESS, - ':common:createFullJarDebug': FROM_CACHE, - ':common:extractDebugAnnotations': FROM_CACHE, - ':common:extractDeepLinksDebug': FROM_CACHE, - ':common:generateDebugAssets': UP_TO_DATE, - ':common:generateDebugBuildConfig': FROM_CACHE, - ':common:generateDebugRFile': FROM_CACHE, - ':common:generateDebugResValues': FROM_CACHE, - ':common:generateDebugResources': UP_TO_DATE, - ':common:javaPreCompileDebug': FROM_CACHE, - ':common:mergeDebugConsumerProguardFiles': SUCCESS, - ':common:mergeDebugGeneratedProguardFiles': SUCCESS, - ':common:mergeDebugJavaResource': SUCCESS, - ':common:mergeDebugJniLibFolders': FROM_CACHE, - ':common:mergeDebugNativeLibs': NO_SOURCE, - ':common:mergeDebugShaders': FROM_CACHE, - ':common:packageDebugAssets': FROM_CACHE, - ':common:packageDebugRenderscript': NO_SOURCE, - ':common:packageDebugResources': FROM_CACHE, - ':common:parseDebugLocalResources': FROM_CACHE, - ':common:preBuild': UP_TO_DATE, - ':common:preDebugBuild': UP_TO_DATE, - ':common:prepareDebugArtProfile': SUCCESS, - ':common:prepareLintJarForPublish': SUCCESS, - ':common:processDebugJavaRes': NO_SOURCE, - ':common:processDebugManifest': FROM_CACHE, - ':common:stripDebugDebugSymbols': NO_SOURCE, - ':common:syncDebugLibJars': FROM_CACHE, - ':common:writeDebugAarMetadata': SUCCESS, - ':dasherdancer:assembleDebug': SUCCESS, - ':dasherdancer:checkDebugAarMetadata': SUCCESS, - ':dasherdancer:checkDebugDuplicateClasses': SUCCESS, - ':dasherdancer:compileDebugAidl': NO_SOURCE, - ':dasherdancer:compileDebugJavaWithJavac': FROM_CACHE, - ':dasherdancer:compileDebugKotlin': FROM_CACHE, - ':dasherdancer:compileDebugRenderscript': NO_SOURCE, - ':dasherdancer:compileDebugShaders': NO_SOURCE, - ':dasherdancer:compressDebugAssets': FROM_CACHE, - ':dasherdancer:createDebugApkListingFileRedirect': SUCCESS, - ':dasherdancer:createDebugCompatibleScreenManifests': SUCCESS, - ':dasherdancer:desugarDebugFileDependencies': FROM_CACHE, - ':dasherdancer:dexBuilderDebug': FROM_CACHE, - ':dasherdancer:extractDeepLinksDebug': FROM_CACHE, - ':dasherdancer:featureDebugWriter': SUCCESS, - ':dasherdancer:generateDebugAssets': UP_TO_DATE, - ':dasherdancer:generateDebugBuildConfig': FROM_CACHE, - ':dasherdancer:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':dasherdancer:generateDebugResValues': FROM_CACHE, - ':dasherdancer:generateDebugResources': UP_TO_DATE, - ':dasherdancer:javaPreCompileDebug': FROM_CACHE, - ':dasherdancer:mapDebugSourceSetPaths': SUCCESS, - ':dasherdancer:mergeDebugAssets': FROM_CACHE, - ':dasherdancer:mergeDebugJavaResource': SUCCESS, - ':dasherdancer:mergeDebugJniLibFolders': FROM_CACHE, - ':dasherdancer:mergeDebugNativeLibs': NO_SOURCE, - ':dasherdancer:mergeDebugResources': SUCCESS, - ':dasherdancer:mergeDebugShaders': FROM_CACHE, - ':dasherdancer:mergeExtDexDebug': FROM_CACHE, - ':dasherdancer:mergeLibDexDebug': FROM_CACHE, - ':dasherdancer:mergeProjectDexDebug': FROM_CACHE, - ':dasherdancer:packageDebug': SUCCESS, - ':dasherdancer:preBuild': UP_TO_DATE, - ':dasherdancer:preDebugBuild': UP_TO_DATE, - ':dasherdancer:processApplicationManifestDebugForBundle': SUCCESS, - ':dasherdancer:processDebugJavaRes': NO_SOURCE, - ':dasherdancer:processDebugMainManifest': FROM_CACHE, - ':dasherdancer:processDebugManifest': FROM_CACHE, - ':dasherdancer:processDebugManifestForPackage': FROM_CACHE, - ':dasherdancer:processDebugResources': FROM_CACHE, - ':dasherdancer:processManifestDebugForFeature': FROM_CACHE, - ':dasherdancer:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:assembleDebug': SUCCESS, - ':doodles-lib:bundleDebugAar': SUCCESS, - ':doodles-lib:bundleLibCompileToJarDebug': SUCCESS, - ':doodles-lib:bundleLibResDebug': NO_SOURCE, - ':doodles-lib:bundleLibRuntimeToJarDebug': SUCCESS, - ':doodles-lib:compileDebugAidl': NO_SOURCE, - ':doodles-lib:compileDebugJavaWithJavac': FROM_CACHE, - ':doodles-lib:compileDebugLibraryResources': FROM_CACHE, - ':doodles-lib:compileDebugRenderscript': NO_SOURCE, - ':doodles-lib:compileDebugShaders': NO_SOURCE, - ':doodles-lib:copyDebugJniLibsProjectAndLocalJars': SUCCESS, - ':doodles-lib:copyDebugJniLibsProjectOnly': SUCCESS, - ':doodles-lib:createFullJarDebug': FROM_CACHE, - ':doodles-lib:extractDebugAnnotations': FROM_CACHE, - ':doodles-lib:extractDeepLinksDebug': FROM_CACHE, - ':doodles-lib:generateDebugAssets': UP_TO_DATE, - ':doodles-lib:generateDebugBuildConfig': FROM_CACHE, - ':doodles-lib:generateDebugRFile': FROM_CACHE, - ':doodles-lib:generateDebugResValues': FROM_CACHE, - ':doodles-lib:generateDebugResources': UP_TO_DATE, - ':doodles-lib:javaPreCompileDebug': FROM_CACHE, - ':doodles-lib:mergeDebugConsumerProguardFiles': SUCCESS, - ':doodles-lib:mergeDebugGeneratedProguardFiles': SUCCESS, - ':doodles-lib:mergeDebugJavaResource': SUCCESS, - ':doodles-lib:mergeDebugJniLibFolders': FROM_CACHE, - ':doodles-lib:mergeDebugNativeLibs': NO_SOURCE, - ':doodles-lib:mergeDebugShaders': FROM_CACHE, - ':doodles-lib:packageDebugAssets': FROM_CACHE, - ':doodles-lib:packageDebugRenderscript': NO_SOURCE, - ':doodles-lib:packageDebugResources': FROM_CACHE, - ':doodles-lib:parseDebugLocalResources': FROM_CACHE, - ':doodles-lib:preBuild': UP_TO_DATE, - ':doodles-lib:preDebugBuild': UP_TO_DATE, - ':doodles-lib:prepareDebugArtProfile': SUCCESS, - ':doodles-lib:prepareLintJarForPublish': SUCCESS, - ':doodles-lib:processDebugJavaRes': NO_SOURCE, - ':doodles-lib:processDebugManifest': FROM_CACHE, - ':doodles-lib:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:syncDebugLibJars': FROM_CACHE, - ':doodles-lib:writeDebugAarMetadata': SUCCESS, - ':gumball:assembleDebug': SUCCESS, - ':gumball:checkDebugAarMetadata': SUCCESS, - ':gumball:checkDebugDuplicateClasses': SUCCESS, - ':gumball:compileDebugAidl': NO_SOURCE, - ':gumball:compileDebugJavaWithJavac': FROM_CACHE, - ':gumball:compileDebugRenderscript': NO_SOURCE, - ':gumball:compileDebugShaders': NO_SOURCE, - ':gumball:compressDebugAssets': FROM_CACHE, - ':gumball:createDebugApkListingFileRedirect': SUCCESS, - ':gumball:createDebugCompatibleScreenManifests': SUCCESS, - ':gumball:desugarDebugFileDependencies': FROM_CACHE, - ':gumball:dexBuilderDebug': FROM_CACHE, - ':gumball:extractDeepLinksDebug': FROM_CACHE, - ':gumball:featureDebugWriter': SUCCESS, - ':gumball:generateDebugAssets': UP_TO_DATE, - ':gumball:generateDebugBuildConfig': FROM_CACHE, - ':gumball:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':gumball:generateDebugResValues': FROM_CACHE, - ':gumball:generateDebugResources': UP_TO_DATE, - ':gumball:javaPreCompileDebug': FROM_CACHE, - ':gumball:mapDebugSourceSetPaths': SUCCESS, - ':gumball:mergeDebugAssets': FROM_CACHE, - ':gumball:mergeDebugJavaResource': SUCCESS, - ':gumball:mergeDebugJniLibFolders': FROM_CACHE, - ':gumball:mergeDebugNativeLibs': NO_SOURCE, - ':gumball:mergeDebugResources': SUCCESS, - ':gumball:mergeDebugShaders': FROM_CACHE, - ':gumball:mergeExtDexDebug': FROM_CACHE, - ':gumball:mergeLibDexDebug': FROM_CACHE, - ':gumball:mergeProjectDexDebug': FROM_CACHE, - ':gumball:packageDebug': SUCCESS, - ':gumball:preBuild': UP_TO_DATE, - ':gumball:preDebugBuild': UP_TO_DATE, - ':gumball:processApplicationManifestDebugForBundle': SUCCESS, - ':gumball:processDebugJavaRes': NO_SOURCE, - ':gumball:processDebugMainManifest': FROM_CACHE, - ':gumball:processDebugManifest': FROM_CACHE, - ':gumball:processDebugManifestForPackage': FROM_CACHE, - ':gumball:processDebugResources': FROM_CACHE, - ':gumball:processManifestDebugForFeature': FROM_CACHE, - ':gumball:stripDebugDebugSymbols': NO_SOURCE, - ':jetpack:assembleDebug': SUCCESS, - ':jetpack:checkDebugAarMetadata': SUCCESS, - ':jetpack:checkDebugDuplicateClasses': SUCCESS, - ':jetpack:compileDebugAidl': NO_SOURCE, - ':jetpack:compileDebugJavaWithJavac': FROM_CACHE, - ':jetpack:compileDebugKotlin': FROM_CACHE, - ':jetpack:compileDebugRenderscript': NO_SOURCE, - ':jetpack:compileDebugShaders': NO_SOURCE, - ':jetpack:compressDebugAssets': FROM_CACHE, - ':jetpack:createDebugApkListingFileRedirect': SUCCESS, - ':jetpack:createDebugCompatibleScreenManifests': SUCCESS, - ':jetpack:desugarDebugFileDependencies': FROM_CACHE, - ':jetpack:dexBuilderDebug': FROM_CACHE, - ':jetpack:extractDeepLinksDebug': FROM_CACHE, - ':jetpack:featureDebugWriter': SUCCESS, - ':jetpack:generateDebugAssets': UP_TO_DATE, - ':jetpack:generateDebugBuildConfig': FROM_CACHE, - ':jetpack:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':jetpack:generateDebugResValues': FROM_CACHE, - ':jetpack:generateDebugResources': UP_TO_DATE, - ':jetpack:javaPreCompileDebug': FROM_CACHE, - ':jetpack:mapDebugSourceSetPaths': SUCCESS, - ':jetpack:mergeDebugAssets': FROM_CACHE, - ':jetpack:mergeDebugJavaResource': SUCCESS, - ':jetpack:mergeDebugJniLibFolders': FROM_CACHE, - ':jetpack:mergeDebugNativeLibs': NO_SOURCE, - ':jetpack:mergeDebugResources': SUCCESS, - ':jetpack:mergeDebugShaders': FROM_CACHE, - ':jetpack:mergeExtDexDebug': FROM_CACHE, - ':jetpack:mergeLibDexDebug': FROM_CACHE, - ':jetpack:mergeProjectDexDebug': FROM_CACHE, - ':jetpack:packageDebug': SUCCESS, - ':jetpack:preBuild': UP_TO_DATE, - ':jetpack:preDebugBuild': UP_TO_DATE, - ':jetpack:processApplicationManifestDebugForBundle': SUCCESS, - ':jetpack:processDebugJavaRes': NO_SOURCE, - ':jetpack:processDebugMainManifest': FROM_CACHE, - ':jetpack:processDebugManifest': FROM_CACHE, - ':jetpack:processDebugManifestForPackage': FROM_CACHE, - ':jetpack:processDebugResources': FROM_CACHE, - ':jetpack:processManifestDebugForFeature': FROM_CACHE, - ':jetpack:stripDebugDebugSymbols': NO_SOURCE, - ':memory:assembleDebug': SUCCESS, - ':memory:checkDebugAarMetadata': SUCCESS, - ':memory:checkDebugDuplicateClasses': SUCCESS, - ':memory:compileDebugAidl': NO_SOURCE, - ':memory:compileDebugJavaWithJavac': FROM_CACHE, - ':memory:compileDebugRenderscript': NO_SOURCE, - ':memory:compileDebugShaders': NO_SOURCE, - ':memory:compressDebugAssets': FROM_CACHE, - ':memory:createDebugApkListingFileRedirect': SUCCESS, - ':memory:createDebugCompatibleScreenManifests': SUCCESS, - ':memory:desugarDebugFileDependencies': FROM_CACHE, - ':memory:dexBuilderDebug': FROM_CACHE, - ':memory:extractDeepLinksDebug': FROM_CACHE, - ':memory:featureDebugWriter': SUCCESS, - ':memory:generateDebugAssets': UP_TO_DATE, - ':memory:generateDebugBuildConfig': FROM_CACHE, - ':memory:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':memory:generateDebugResValues': FROM_CACHE, - ':memory:generateDebugResources': UP_TO_DATE, - ':memory:javaPreCompileDebug': FROM_CACHE, - ':memory:mapDebugSourceSetPaths': SUCCESS, - ':memory:mergeDebugAssets': FROM_CACHE, - ':memory:mergeDebugJavaResource': SUCCESS, - ':memory:mergeDebugJniLibFolders': FROM_CACHE, - ':memory:mergeDebugNativeLibs': NO_SOURCE, - ':memory:mergeDebugResources': SUCCESS, - ':memory:mergeDebugShaders': FROM_CACHE, - ':memory:mergeExtDexDebug': FROM_CACHE, - ':memory:mergeLibDexDebug': FROM_CACHE, - ':memory:mergeProjectDexDebug': FROM_CACHE, - ':memory:packageDebug': SUCCESS, - ':memory:preBuild': UP_TO_DATE, - ':memory:preDebugBuild': UP_TO_DATE, - ':memory:processApplicationManifestDebugForBundle': SUCCESS, - ':memory:processDebugJavaRes': NO_SOURCE, - ':memory:processDebugMainManifest': FROM_CACHE, - ':memory:processDebugManifest': FROM_CACHE, - ':memory:processDebugManifestForPackage': FROM_CACHE, - ':memory:processDebugResources': FROM_CACHE, - ':memory:processManifestDebugForFeature': FROM_CACHE, - ':memory:stripDebugDebugSymbols': NO_SOURCE, - ':penguinswim:assembleDebug': SUCCESS, - ':penguinswim:checkDebugAarMetadata': SUCCESS, - ':penguinswim:checkDebugDuplicateClasses': SUCCESS, - ':penguinswim:compileDebugAidl': NO_SOURCE, - ':penguinswim:compileDebugJavaWithJavac': FROM_CACHE, - ':penguinswim:compileDebugRenderscript': NO_SOURCE, - ':penguinswim:compileDebugShaders': NO_SOURCE, - ':penguinswim:compressDebugAssets': FROM_CACHE, - ':penguinswim:createDebugApkListingFileRedirect': SUCCESS, - ':penguinswim:createDebugCompatibleScreenManifests': SUCCESS, - ':penguinswim:desugarDebugFileDependencies': FROM_CACHE, - ':penguinswim:dexBuilderDebug': FROM_CACHE, - ':penguinswim:extractDeepLinksDebug': FROM_CACHE, - ':penguinswim:featureDebugWriter': SUCCESS, - ':penguinswim:generateDebugAssets': UP_TO_DATE, - ':penguinswim:generateDebugBuildConfig': FROM_CACHE, - ':penguinswim:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':penguinswim:generateDebugResValues': FROM_CACHE, - ':penguinswim:generateDebugResources': UP_TO_DATE, - ':penguinswim:javaPreCompileDebug': FROM_CACHE, - ':penguinswim:mapDebugSourceSetPaths': SUCCESS, - ':penguinswim:mergeDebugAssets': FROM_CACHE, - ':penguinswim:mergeDebugJavaResource': SUCCESS, - ':penguinswim:mergeDebugJniLibFolders': FROM_CACHE, - ':penguinswim:mergeDebugNativeLibs': NO_SOURCE, - ':penguinswim:mergeDebugResources': SUCCESS, - ':penguinswim:mergeDebugShaders': FROM_CACHE, - ':penguinswim:mergeExtDexDebug': FROM_CACHE, - ':penguinswim:mergeLibDexDebug': FROM_CACHE, - ':penguinswim:mergeProjectDexDebug': FROM_CACHE, - ':penguinswim:packageDebug': SUCCESS, - ':penguinswim:preBuild': UP_TO_DATE, - ':penguinswim:preDebugBuild': UP_TO_DATE, - ':penguinswim:processApplicationManifestDebugForBundle': SUCCESS, - ':penguinswim:processDebugJavaRes': NO_SOURCE, - ':penguinswim:processDebugMainManifest': FROM_CACHE, - ':penguinswim:processDebugManifest': FROM_CACHE, - ':penguinswim:processDebugManifestForPackage': FROM_CACHE, - ':penguinswim:processDebugResources': FROM_CACHE, - ':penguinswim:processManifestDebugForFeature': FROM_CACHE, - ':penguinswim:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:assembleDebug': SUCCESS, - ':playgames:bundleDebugAar': SUCCESS, - ':playgames:bundleLibCompileToJarDebug': SUCCESS, - ':playgames:bundleLibResDebug': NO_SOURCE, - ':playgames:bundleLibRuntimeToJarDebug': SUCCESS, - ':playgames:compileDebugAidl': NO_SOURCE, - ':playgames:compileDebugJavaWithJavac': FROM_CACHE, - ':playgames:compileDebugLibraryResources': FROM_CACHE, - ':playgames:compileDebugRenderscript': NO_SOURCE, - ':playgames:compileDebugShaders': NO_SOURCE, - ':playgames:copyDebugJniLibsProjectAndLocalJars': SUCCESS, - ':playgames:copyDebugJniLibsProjectOnly': SUCCESS, - ':playgames:createFullJarDebug': FROM_CACHE, - ':playgames:extractDebugAnnotations': FROM_CACHE, - ':playgames:extractDeepLinksDebug': FROM_CACHE, - ':playgames:generateDebugAssets': UP_TO_DATE, - ':playgames:generateDebugBuildConfig': FROM_CACHE, - ':playgames:generateDebugRFile': FROM_CACHE, - ':playgames:generateDebugResValues': FROM_CACHE, - ':playgames:generateDebugResources': UP_TO_DATE, - ':playgames:javaPreCompileDebug': FROM_CACHE, - ':playgames:mergeDebugConsumerProguardFiles': SUCCESS, - ':playgames:mergeDebugGeneratedProguardFiles': SUCCESS, - ':playgames:mergeDebugJavaResource': SUCCESS, - ':playgames:mergeDebugJniLibFolders': FROM_CACHE, - ':playgames:mergeDebugNativeLibs': NO_SOURCE, - ':playgames:mergeDebugShaders': FROM_CACHE, - ':playgames:packageDebugAssets': FROM_CACHE, - ':playgames:packageDebugRenderscript': NO_SOURCE, - ':playgames:packageDebugResources': FROM_CACHE, - ':playgames:parseDebugLocalResources': FROM_CACHE, - ':playgames:preBuild': UP_TO_DATE, - ':playgames:preDebugBuild': UP_TO_DATE, - ':playgames:prepareDebugArtProfile': SUCCESS, - ':playgames:prepareLintJarForPublish': SUCCESS, - ':playgames:processDebugJavaRes': NO_SOURCE, - ':playgames:processDebugManifest': FROM_CACHE, - ':playgames:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:syncDebugLibJars': FROM_CACHE, - ':playgames:writeDebugAarMetadata': SUCCESS, - ':presenttoss:assembleDebug': SUCCESS, - ':presenttoss:checkDebugAarMetadata': SUCCESS, - ':presenttoss:checkDebugDuplicateClasses': SUCCESS, - ':presenttoss:compileDebugAidl': NO_SOURCE, - ':presenttoss:compileDebugJavaWithJavac': FROM_CACHE, - ':presenttoss:compileDebugRenderscript': NO_SOURCE, - ':presenttoss:compileDebugShaders': NO_SOURCE, - ':presenttoss:compressDebugAssets': FROM_CACHE, - ':presenttoss:createDebugApkListingFileRedirect': SUCCESS, - ':presenttoss:createDebugCompatibleScreenManifests': SUCCESS, - ':presenttoss:desugarDebugFileDependencies': FROM_CACHE, - ':presenttoss:dexBuilderDebug': FROM_CACHE, - ':presenttoss:extractDeepLinksDebug': FROM_CACHE, - ':presenttoss:featureDebugWriter': SUCCESS, - ':presenttoss:generateDebugAssets': UP_TO_DATE, - ':presenttoss:generateDebugBuildConfig': FROM_CACHE, - ':presenttoss:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':presenttoss:generateDebugResValues': FROM_CACHE, - ':presenttoss:generateDebugResources': UP_TO_DATE, - ':presenttoss:javaPreCompileDebug': FROM_CACHE, - ':presenttoss:mapDebugSourceSetPaths': SUCCESS, - ':presenttoss:mergeDebugAssets': FROM_CACHE, - ':presenttoss:mergeDebugJavaResource': SUCCESS, - ':presenttoss:mergeDebugJniLibFolders': FROM_CACHE, - ':presenttoss:mergeDebugNativeLibs': NO_SOURCE, - ':presenttoss:mergeDebugResources': SUCCESS, - ':presenttoss:mergeDebugShaders': FROM_CACHE, - ':presenttoss:mergeExtDexDebug': FROM_CACHE, - ':presenttoss:mergeLibDexDebug': FROM_CACHE, - ':presenttoss:mergeProjectDexDebug': FROM_CACHE, - ':presenttoss:packageDebug': SUCCESS, - ':presenttoss:preBuild': UP_TO_DATE, - ':presenttoss:preDebugBuild': UP_TO_DATE, - ':presenttoss:processApplicationManifestDebugForBundle': SUCCESS, - ':presenttoss:processDebugJavaRes': NO_SOURCE, - ':presenttoss:processDebugMainManifest': FROM_CACHE, - ':presenttoss:processDebugManifest': FROM_CACHE, - ':presenttoss:processDebugManifestForPackage': FROM_CACHE, - ':presenttoss:processDebugResources': FROM_CACHE, - ':presenttoss:processManifestDebugForFeature': FROM_CACHE, - ':presenttoss:stripDebugDebugSymbols': NO_SOURCE, - ':rocketsleigh:assembleDebug': SUCCESS, - ':rocketsleigh:checkDebugAarMetadata': SUCCESS, - ':rocketsleigh:checkDebugDuplicateClasses': SUCCESS, - ':rocketsleigh:compileDebugAidl': NO_SOURCE, - ':rocketsleigh:compileDebugJavaWithJavac': FROM_CACHE, - ':rocketsleigh:compileDebugKotlin': FROM_CACHE, - ':rocketsleigh:compileDebugRenderscript': NO_SOURCE, - ':rocketsleigh:compileDebugShaders': NO_SOURCE, - ':rocketsleigh:compressDebugAssets': FROM_CACHE, - ':rocketsleigh:createDebugApkListingFileRedirect': SUCCESS, - ':rocketsleigh:createDebugCompatibleScreenManifests': SUCCESS, - ':rocketsleigh:desugarDebugFileDependencies': FROM_CACHE, - ':rocketsleigh:dexBuilderDebug': FROM_CACHE, - ':rocketsleigh:extractDeepLinksDebug': FROM_CACHE, - ':rocketsleigh:featureDebugWriter': SUCCESS, - ':rocketsleigh:generateDebugAssets': UP_TO_DATE, - ':rocketsleigh:generateDebugBuildConfig': FROM_CACHE, - ':rocketsleigh:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':rocketsleigh:generateDebugResValues': FROM_CACHE, - ':rocketsleigh:generateDebugResources': UP_TO_DATE, - ':rocketsleigh:javaPreCompileDebug': FROM_CACHE, - ':rocketsleigh:mapDebugSourceSetPaths': SUCCESS, - ':rocketsleigh:mergeDebugAssets': FROM_CACHE, - ':rocketsleigh:mergeDebugJavaResource': SUCCESS, - ':rocketsleigh:mergeDebugJniLibFolders': FROM_CACHE, - ':rocketsleigh:mergeDebugNativeLibs': NO_SOURCE, - ':rocketsleigh:mergeDebugResources': SUCCESS, - ':rocketsleigh:mergeDebugShaders': FROM_CACHE, - ':rocketsleigh:mergeExtDexDebug': FROM_CACHE, - ':rocketsleigh:mergeLibDexDebug': FROM_CACHE, - ':rocketsleigh:mergeProjectDexDebug': FROM_CACHE, - ':rocketsleigh:packageDebug': SUCCESS, - ':rocketsleigh:preBuild': UP_TO_DATE, - ':rocketsleigh:preDebugBuild': UP_TO_DATE, - ':rocketsleigh:processApplicationManifestDebugForBundle': SUCCESS, - ':rocketsleigh:processDebugJavaRes': NO_SOURCE, - ':rocketsleigh:processDebugMainManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifestForPackage': FROM_CACHE, - ':rocketsleigh:processDebugResources': FROM_CACHE, - ':rocketsleigh:processManifestDebugForFeature': FROM_CACHE, - ':rocketsleigh:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:assembleDebug': SUCCESS, - ':santa-tracker:bundleDebugClassesToCompileJar': SUCCESS, - ':santa-tracker:bundleDebugClassesToRuntimeJar': SUCCESS, - ':santa-tracker:checkDebugAarMetadata': SUCCESS, - ':santa-tracker:checkDebugDuplicateClasses': SUCCESS, - ':santa-tracker:checkDebugLibraries': FROM_CACHE, - ':santa-tracker:compileDebugAidl': NO_SOURCE, - ':santa-tracker:compileDebugJavaWithJavac': FROM_CACHE, - ':santa-tracker:compileDebugKotlin': FROM_CACHE, - ':santa-tracker:compileDebugRenderscript': NO_SOURCE, - ':santa-tracker:compileDebugShaders': NO_SOURCE, - ':santa-tracker:compressDebugAssets': FROM_CACHE, - ':santa-tracker:createDebugApkListingFileRedirect': SUCCESS, - ':santa-tracker:createDebugCompatibleScreenManifests': SUCCESS, - ':santa-tracker:desugarDebugFileDependencies': FROM_CACHE, - ':santa-tracker:dexBuilderDebug': FROM_CACHE, - ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, - ':santa-tracker:generateDebugAssets': UP_TO_DATE, - ':santa-tracker:generateDebugBuildConfig': FROM_CACHE, - ':santa-tracker:generateDebugFeatureMetadata': FROM_CACHE, - ':santa-tracker:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':santa-tracker:generateDebugResValues': FROM_CACHE, - ':santa-tracker:generateDebugResources': UP_TO_DATE, - ':santa-tracker:handleDebugMicroApk': FROM_CACHE, - ':santa-tracker:javaPreCompileDebug': FROM_CACHE, - ':santa-tracker:kaptDebugKotlin': FROM_CACHE, - ':santa-tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':santa-tracker:mapDebugSourceSetPaths': SUCCESS, - ':santa-tracker:mergeDebugAssets': FROM_CACHE, - ':santa-tracker:mergeDebugJavaResource': SUCCESS, - ':santa-tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':santa-tracker:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':santa-tracker:mergeDebugNativeLibs': NO_SOURCE, - ':santa-tracker:mergeDebugResources': SUCCESS, - ':santa-tracker:mergeDebugShaders': FROM_CACHE, - ':santa-tracker:mergeExtDexDebug': FROM_CACHE, - ':santa-tracker:mergeLibDexDebug': FROM_CACHE, - ':santa-tracker:mergeProjectDexDebug': FROM_CACHE, - ':santa-tracker:packageDebug': SUCCESS, - ':santa-tracker:preBuild': UP_TO_DATE, - ':santa-tracker:preDebugBuild': SUCCESS, - ':santa-tracker:processDebugJavaRes': NO_SOURCE, - ':santa-tracker:processDebugMainManifest': FROM_CACHE, - ':santa-tracker:processDebugManifest': FROM_CACHE, - ':santa-tracker:processDebugManifestForPackage': FROM_CACHE, - ':santa-tracker:processDebugResources': FROM_CACHE, - ':santa-tracker:signingConfigWriterDebug': SUCCESS, - ':santa-tracker:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:validateSigningDebug': SUCCESS, - ':santa-tracker:writeDebugAppMetadata': SUCCESS, - ':santa-tracker:writeDebugModuleMetadata': SUCCESS, - ':santa-tracker:writeDebugSigningConfigVersions': SUCCESS, - ':snowballrun:assembleDebug': SUCCESS, - ':snowballrun:checkDebugAarMetadata': SUCCESS, - ':snowballrun:checkDebugDuplicateClasses': SUCCESS, - ':snowballrun:compileDebugAidl': NO_SOURCE, - ':snowballrun:compileDebugJavaWithJavac': FROM_CACHE, - ':snowballrun:compileDebugRenderscript': NO_SOURCE, - ':snowballrun:compileDebugShaders': NO_SOURCE, - ':snowballrun:compressDebugAssets': FROM_CACHE, - ':snowballrun:createDebugApkListingFileRedirect': SUCCESS, - ':snowballrun:createDebugCompatibleScreenManifests': SUCCESS, - ':snowballrun:desugarDebugFileDependencies': FROM_CACHE, - ':snowballrun:dexBuilderDebug': FROM_CACHE, - ':snowballrun:extractDeepLinksDebug': FROM_CACHE, - ':snowballrun:featureDebugWriter': SUCCESS, - ':snowballrun:generateDebugAssets': UP_TO_DATE, - ':snowballrun:generateDebugBuildConfig': FROM_CACHE, - ':snowballrun:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':snowballrun:generateDebugResValues': FROM_CACHE, - ':snowballrun:generateDebugResources': UP_TO_DATE, - ':snowballrun:javaPreCompileDebug': FROM_CACHE, - ':snowballrun:mapDebugSourceSetPaths': SUCCESS, - ':snowballrun:mergeDebugAssets': FROM_CACHE, - ':snowballrun:mergeDebugJavaResource': SUCCESS, - ':snowballrun:mergeDebugJniLibFolders': FROM_CACHE, - ':snowballrun:mergeDebugNativeLibs': NO_SOURCE, - ':snowballrun:mergeDebugResources': SUCCESS, - ':snowballrun:mergeDebugShaders': FROM_CACHE, - ':snowballrun:mergeExtDexDebug': FROM_CACHE, - ':snowballrun:mergeLibDexDebug': FROM_CACHE, - ':snowballrun:mergeProjectDexDebug': FROM_CACHE, - ':snowballrun:packageDebug': SUCCESS, - ':snowballrun:preBuild': UP_TO_DATE, - ':snowballrun:preDebugBuild': UP_TO_DATE, - ':snowballrun:processApplicationManifestDebugForBundle': SUCCESS, - ':snowballrun:processDebugJavaRes': NO_SOURCE, - ':snowballrun:processDebugMainManifest': FROM_CACHE, - ':snowballrun:processDebugManifest': FROM_CACHE, - ':snowballrun:processDebugManifestForPackage': FROM_CACHE, - ':snowballrun:processDebugResources': FROM_CACHE, - ':snowballrun:processManifestDebugForFeature': FROM_CACHE, - ':snowballrun:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:assembleDebug': SUCCESS, - ':tracker:bundleDebugAar': SUCCESS, - ':tracker:bundleLibCompileToJarDebug': SUCCESS, - ':tracker:bundleLibResDebug': SUCCESS, - ':tracker:bundleLibRuntimeToJarDebug': SUCCESS, - ':tracker:compileDebugAidl': NO_SOURCE, - ':tracker:compileDebugJavaWithJavac': SUCCESS, - ':tracker:compileDebugKotlin': FROM_CACHE, - ':tracker:compileDebugLibraryResources': FROM_CACHE, - ':tracker:compileDebugRenderscript': NO_SOURCE, - ':tracker:compileDebugShaders': NO_SOURCE, - ':tracker:copyDebugJniLibsProjectAndLocalJars': SUCCESS, - ':tracker:copyDebugJniLibsProjectOnly': SUCCESS, - ':tracker:createFullJarDebug': FROM_CACHE, - ':tracker:extractDebugAnnotations': FROM_CACHE, - ':tracker:extractDeepLinksDebug': FROM_CACHE, - ':tracker:generateDebugAssets': UP_TO_DATE, - ':tracker:generateDebugBuildConfig': FROM_CACHE, - ':tracker:generateDebugRFile': FROM_CACHE, - ':tracker:generateDebugResValues': FROM_CACHE, - ':tracker:generateDebugResources': UP_TO_DATE, - ':tracker:javaPreCompileDebug': FROM_CACHE, - ':tracker:kaptDebugKotlin': FROM_CACHE, - ':tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':tracker:mergeDebugConsumerProguardFiles': SUCCESS, - ':tracker:mergeDebugGeneratedProguardFiles': SUCCESS, - ':tracker:mergeDebugJavaResource': SUCCESS, - ':tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':tracker:mergeDebugNativeLibs': NO_SOURCE, - ':tracker:mergeDebugShaders': FROM_CACHE, - ':tracker:packageDebugAssets': FROM_CACHE, - ':tracker:packageDebugRenderscript': NO_SOURCE, - ':tracker:packageDebugResources': FROM_CACHE, - ':tracker:parseDebugLocalResources': FROM_CACHE, - ':tracker:preBuild': UP_TO_DATE, - ':tracker:preDebugBuild': UP_TO_DATE, - ':tracker:prepareDebugArtProfile': SUCCESS, - ':tracker:prepareLintJarForPublish': SUCCESS, - ':tracker:processDebugJavaRes': NO_SOURCE, - ':tracker:processDebugManifest': FROM_CACHE, - ':tracker:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:syncDebugLibJars': FROM_CACHE, - ':tracker:writeDebugAarMetadata': SUCCESS, - ':wearable:assembleDebug': SUCCESS, - ':wearable:checkDebugAarMetadata': SUCCESS, - ':wearable:checkDebugDuplicateClasses': SUCCESS, - ':wearable:compileDebugAidl': NO_SOURCE, - ':wearable:compileDebugJavaWithJavac': FROM_CACHE, - ':wearable:compileDebugKotlin': FROM_CACHE, - ':wearable:compileDebugRenderscript': NO_SOURCE, - ':wearable:compileDebugShaders': NO_SOURCE, - ':wearable:compressDebugAssets': FROM_CACHE, - ':wearable:createDebugApkListingFileRedirect': SUCCESS, - ':wearable:createDebugCompatibleScreenManifests': SUCCESS, - ':wearable:desugarDebugFileDependencies': FROM_CACHE, - ':wearable:dexBuilderDebug': FROM_CACHE, - ':wearable:extractDeepLinksDebug': FROM_CACHE, - ':wearable:generateDebugAssets': UP_TO_DATE, - ':wearable:generateDebugBuildConfig': FROM_CACHE, - ':wearable:generateDebugResValues': FROM_CACHE, - ':wearable:generateDebugResources': UP_TO_DATE, - ':wearable:javaPreCompileDebug': FROM_CACHE, - ':wearable:kaptDebugKotlin': SKIPPED, - ':wearable:kaptGenerateStubsDebugKotlin': SKIPPED, - ':wearable:mapDebugSourceSetPaths': SUCCESS, - ':wearable:mergeDebugAssets': FROM_CACHE, - ':wearable:mergeDebugJavaResource': SUCCESS, - ':wearable:mergeDebugJniLibFolders': FROM_CACHE, - ':wearable:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':wearable:mergeDebugNativeLibs': NO_SOURCE, - ':wearable:mergeDebugResources': SUCCESS, - ':wearable:mergeDebugShaders': FROM_CACHE, - ':wearable:mergeExtDexDebug': FROM_CACHE, - ':wearable:mergeLibDexDebug': FROM_CACHE, - ':wearable:mergeProjectDexDebug': FROM_CACHE, - ':wearable:packageDebug': SUCCESS, - ':wearable:preBuild': UP_TO_DATE, - ':wearable:preDebugBuild': UP_TO_DATE, - ':wearable:processDebugJavaRes': NO_SOURCE, - ':wearable:processDebugMainManifest': FROM_CACHE, - ':wearable:processDebugManifest': FROM_CACHE, - ':wearable:processDebugManifestForPackage': FROM_CACHE, - ':wearable:processDebugResources': FROM_CACHE, - ':wearable:stripDebugDebugSymbols': NO_SOURCE, - ':wearable:validateSigningDebug': SUCCESS, - ':wearable:writeDebugAppMetadata': SUCCESS, - ':wearable:writeDebugSigningConfigVersions': SUCCESS, - ] -} - -class AndroidPluginExpectations71 { - - static final EXPECTED_RESULTS_7_1 = [ - ':cityquiz:assembleDebug': SUCCESS, - ':cityquiz:checkDebugAarMetadata': FROM_CACHE, - ':cityquiz:checkDebugDuplicateClasses': FROM_CACHE, - ':cityquiz:compileDebugAidl': NO_SOURCE, - ':cityquiz:compileDebugJavaWithJavac': FROM_CACHE, - ':cityquiz:compileDebugKotlin': FROM_CACHE, - ':cityquiz:compileDebugRenderscript': NO_SOURCE, - ':cityquiz:compileDebugShaders': NO_SOURCE, - ':cityquiz:compressDebugAssets': FROM_CACHE, - ':cityquiz:createDebugApkListingFileRedirect': SUCCESS, - ':cityquiz:createDebugCompatibleScreenManifests': FROM_CACHE, - ':cityquiz:desugarDebugFileDependencies': FROM_CACHE, - ':cityquiz:dexBuilderDebug': FROM_CACHE, - ':cityquiz:extractDeepLinksDebug': FROM_CACHE, - ':cityquiz:featureDebugWriter': SUCCESS, - ':cityquiz:generateDebugAssets': UP_TO_DATE, - ':cityquiz:generateDebugBuildConfig': FROM_CACHE, - ':cityquiz:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':cityquiz:generateDebugResValues': FROM_CACHE, - ':cityquiz:generateDebugResources': UP_TO_DATE, - ':cityquiz:javaPreCompileDebug': FROM_CACHE, - ':cityquiz:mergeDebugAssets': FROM_CACHE, - ':cityquiz:mergeDebugJavaResource': SUCCESS, - ':cityquiz:mergeDebugJniLibFolders': FROM_CACHE, - ':cityquiz:mergeDebugNativeLibs': NO_SOURCE, - ':cityquiz:mergeDebugResources': SUCCESS, - ':cityquiz:mergeDebugShaders': FROM_CACHE, - ':cityquiz:mergeExtDexDebug': FROM_CACHE, - ':cityquiz:mergeLibDexDebug': FROM_CACHE, - ':cityquiz:mergeProjectDexDebug': FROM_CACHE, - ':cityquiz:packageDebug': SUCCESS, - ':cityquiz:preBuild': UP_TO_DATE, - ':cityquiz:preDebugBuild': UP_TO_DATE, - ':cityquiz:processApplicationManifestDebugForBundle': FROM_CACHE, - ':cityquiz:processDebugJavaRes': NO_SOURCE, - ':cityquiz:processDebugMainManifest': FROM_CACHE, - ':cityquiz:processDebugManifest': FROM_CACHE, - ':cityquiz:processDebugManifestForPackage': FROM_CACHE, - ':cityquiz:processDebugResources': SUCCESS, - ':cityquiz:processManifestDebugForFeature': FROM_CACHE, - ':cityquiz:stripDebugDebugSymbols': NO_SOURCE, - ':common:assembleDebug': SUCCESS, - ':common:bundleDebugAar': SUCCESS, - ':common:bundleLibCompileToJarDebug': FROM_CACHE, - ':common:bundleLibResDebug': FROM_CACHE, - ':common:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':common:compileDebugAidl': NO_SOURCE, - ':common:compileDebugJavaWithJavac': FROM_CACHE, - ':common:compileDebugKotlin': FROM_CACHE, - ':common:compileDebugLibraryResources': SUCCESS, - ':common:compileDebugRenderscript': NO_SOURCE, - ':common:compileDebugShaders': NO_SOURCE, - ':common:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':common:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':common:createFullJarDebug': FROM_CACHE, - ':common:extractDebugAnnotations': FROM_CACHE, - ':common:extractDeepLinksDebug': FROM_CACHE, - ':common:generateDebugAssets': UP_TO_DATE, - ':common:generateDebugBuildConfig': FROM_CACHE, - ':common:generateDebugRFile': FROM_CACHE, - ':common:generateDebugResValues': FROM_CACHE, - ':common:generateDebugResources': UP_TO_DATE, - ':common:javaPreCompileDebug': FROM_CACHE, - ':common:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':common:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':common:mergeDebugJavaResource': SUCCESS, - ':common:mergeDebugJniLibFolders': FROM_CACHE, - ':common:mergeDebugNativeLibs': NO_SOURCE, - ':common:mergeDebugShaders': FROM_CACHE, - ':common:packageDebugAssets': FROM_CACHE, - ':common:packageDebugRenderscript': NO_SOURCE, - ':common:packageDebugResources': FROM_CACHE, - ':common:parseDebugLocalResources': FROM_CACHE, - ':common:preBuild': UP_TO_DATE, - ':common:preDebugBuild': UP_TO_DATE, - ':common:prepareDebugArtProfile': SUCCESS, - ':common:prepareLintJarForPublish': SUCCESS, - ':common:processDebugJavaRes': NO_SOURCE, - ':common:processDebugManifest': FROM_CACHE, - ':common:stripDebugDebugSymbols': NO_SOURCE, - ':common:syncDebugLibJars': FROM_CACHE, - ':common:writeDebugAarMetadata': FROM_CACHE, - ':dasherdancer:assembleDebug': SUCCESS, - ':dasherdancer:checkDebugAarMetadata': FROM_CACHE, - ':dasherdancer:checkDebugDuplicateClasses': FROM_CACHE, - ':dasherdancer:compileDebugAidl': NO_SOURCE, - ':dasherdancer:compileDebugJavaWithJavac': FROM_CACHE, - ':dasherdancer:compileDebugKotlin': FROM_CACHE, - ':dasherdancer:compileDebugRenderscript': NO_SOURCE, - ':dasherdancer:compileDebugShaders': NO_SOURCE, - ':dasherdancer:compressDebugAssets': FROM_CACHE, - ':dasherdancer:createDebugApkListingFileRedirect': SUCCESS, - ':dasherdancer:createDebugCompatibleScreenManifests': FROM_CACHE, - ':dasherdancer:desugarDebugFileDependencies': FROM_CACHE, - ':dasherdancer:dexBuilderDebug': FROM_CACHE, - ':dasherdancer:extractDeepLinksDebug': FROM_CACHE, - ':dasherdancer:featureDebugWriter': SUCCESS, - ':dasherdancer:generateDebugAssets': UP_TO_DATE, - ':dasherdancer:generateDebugBuildConfig': FROM_CACHE, - ':dasherdancer:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':dasherdancer:generateDebugResValues': FROM_CACHE, - ':dasherdancer:generateDebugResources': UP_TO_DATE, - ':dasherdancer:javaPreCompileDebug': FROM_CACHE, - ':dasherdancer:mergeDebugAssets': FROM_CACHE, - ':dasherdancer:mergeDebugJavaResource': SUCCESS, - ':dasherdancer:mergeDebugJniLibFolders': FROM_CACHE, - ':dasherdancer:mergeDebugNativeLibs': NO_SOURCE, - ':dasherdancer:mergeDebugResources': SUCCESS, - ':dasherdancer:mergeDebugShaders': FROM_CACHE, - ':dasherdancer:mergeExtDexDebug': FROM_CACHE, - ':dasherdancer:mergeLibDexDebug': FROM_CACHE, - ':dasherdancer:mergeProjectDexDebug': FROM_CACHE, - ':dasherdancer:packageDebug': SUCCESS, - ':dasherdancer:preBuild': UP_TO_DATE, - ':dasherdancer:preDebugBuild': UP_TO_DATE, - ':dasherdancer:processApplicationManifestDebugForBundle': FROM_CACHE, - ':dasherdancer:processDebugJavaRes': NO_SOURCE, - ':dasherdancer:processDebugMainManifest': FROM_CACHE, - ':dasherdancer:processDebugManifest': FROM_CACHE, - ':dasherdancer:processDebugManifestForPackage': FROM_CACHE, - ':dasherdancer:processDebugResources': SUCCESS, - ':dasherdancer:processManifestDebugForFeature': FROM_CACHE, - ':dasherdancer:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:assembleDebug': SUCCESS, - ':doodles-lib:bundleDebugAar': SUCCESS, - ':doodles-lib:bundleLibCompileToJarDebug': FROM_CACHE, - ':doodles-lib:bundleLibResDebug': NO_SOURCE, - ':doodles-lib:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':doodles-lib:compileDebugAidl': NO_SOURCE, - ':doodles-lib:compileDebugJavaWithJavac': FROM_CACHE, - ':doodles-lib:compileDebugLibraryResources': SUCCESS, - ':doodles-lib:compileDebugRenderscript': NO_SOURCE, - ':doodles-lib:compileDebugShaders': NO_SOURCE, - ':doodles-lib:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':doodles-lib:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':doodles-lib:createFullJarDebug': FROM_CACHE, - ':doodles-lib:extractDebugAnnotations': FROM_CACHE, - ':doodles-lib:extractDeepLinksDebug': FROM_CACHE, - ':doodles-lib:generateDebugAssets': UP_TO_DATE, - ':doodles-lib:generateDebugBuildConfig': FROM_CACHE, - ':doodles-lib:generateDebugRFile': FROM_CACHE, - ':doodles-lib:generateDebugResValues': FROM_CACHE, - ':doodles-lib:generateDebugResources': UP_TO_DATE, - ':doodles-lib:javaPreCompileDebug': FROM_CACHE, - ':doodles-lib:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugJavaResource': SUCCESS, - ':doodles-lib:mergeDebugJniLibFolders': FROM_CACHE, - ':doodles-lib:mergeDebugNativeLibs': NO_SOURCE, - ':doodles-lib:mergeDebugShaders': FROM_CACHE, - ':doodles-lib:packageDebugAssets': FROM_CACHE, - ':doodles-lib:packageDebugRenderscript': NO_SOURCE, - ':doodles-lib:packageDebugResources': FROM_CACHE, - ':doodles-lib:parseDebugLocalResources': FROM_CACHE, - ':doodles-lib:preBuild': UP_TO_DATE, - ':doodles-lib:preDebugBuild': UP_TO_DATE, - ':doodles-lib:prepareDebugArtProfile': SUCCESS, - ':doodles-lib:prepareLintJarForPublish': SUCCESS, - ':doodles-lib:processDebugJavaRes': NO_SOURCE, - ':doodles-lib:processDebugManifest': FROM_CACHE, - ':doodles-lib:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:syncDebugLibJars': FROM_CACHE, - ':doodles-lib:writeDebugAarMetadata': FROM_CACHE, - ':gumball:assembleDebug': SUCCESS, - ':gumball:checkDebugAarMetadata': FROM_CACHE, - ':gumball:checkDebugDuplicateClasses': FROM_CACHE, - ':gumball:compileDebugAidl': NO_SOURCE, - ':gumball:compileDebugJavaWithJavac': FROM_CACHE, - ':gumball:compileDebugRenderscript': NO_SOURCE, - ':gumball:compileDebugShaders': NO_SOURCE, - ':gumball:compressDebugAssets': FROM_CACHE, - ':gumball:createDebugApkListingFileRedirect': SUCCESS, - ':gumball:createDebugCompatibleScreenManifests': FROM_CACHE, - ':gumball:desugarDebugFileDependencies': FROM_CACHE, - ':gumball:dexBuilderDebug': FROM_CACHE, - ':gumball:extractDeepLinksDebug': FROM_CACHE, - ':gumball:featureDebugWriter': SUCCESS, - ':gumball:generateDebugAssets': UP_TO_DATE, - ':gumball:generateDebugBuildConfig': FROM_CACHE, - ':gumball:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':gumball:generateDebugResValues': FROM_CACHE, - ':gumball:generateDebugResources': UP_TO_DATE, - ':gumball:javaPreCompileDebug': FROM_CACHE, - ':gumball:mergeDebugAssets': FROM_CACHE, - ':gumball:mergeDebugJavaResource': SUCCESS, - ':gumball:mergeDebugJniLibFolders': FROM_CACHE, - ':gumball:mergeDebugNativeLibs': NO_SOURCE, - ':gumball:mergeDebugResources': SUCCESS, - ':gumball:mergeDebugShaders': FROM_CACHE, - ':gumball:mergeExtDexDebug': FROM_CACHE, - ':gumball:mergeLibDexDebug': FROM_CACHE, - ':gumball:mergeProjectDexDebug': FROM_CACHE, - ':gumball:packageDebug': SUCCESS, - ':gumball:preBuild': UP_TO_DATE, - ':gumball:preDebugBuild': UP_TO_DATE, - ':gumball:processApplicationManifestDebugForBundle': FROM_CACHE, - ':gumball:processDebugJavaRes': NO_SOURCE, - ':gumball:processDebugMainManifest': FROM_CACHE, - ':gumball:processDebugManifest': FROM_CACHE, - ':gumball:processDebugManifestForPackage': FROM_CACHE, - ':gumball:processDebugResources': SUCCESS, - ':gumball:processManifestDebugForFeature': FROM_CACHE, - ':gumball:stripDebugDebugSymbols': NO_SOURCE, - ':jetpack:assembleDebug': SUCCESS, - ':jetpack:checkDebugAarMetadata': FROM_CACHE, - ':jetpack:checkDebugDuplicateClasses': FROM_CACHE, - ':jetpack:compileDebugAidl': NO_SOURCE, - ':jetpack:compileDebugJavaWithJavac': FROM_CACHE, - ':jetpack:compileDebugKotlin': FROM_CACHE, - ':jetpack:compileDebugRenderscript': NO_SOURCE, - ':jetpack:compileDebugShaders': NO_SOURCE, - ':jetpack:compressDebugAssets': FROM_CACHE, - ':jetpack:createDebugApkListingFileRedirect': SUCCESS, - ':jetpack:createDebugCompatibleScreenManifests': FROM_CACHE, - ':jetpack:desugarDebugFileDependencies': FROM_CACHE, - ':jetpack:dexBuilderDebug': FROM_CACHE, - ':jetpack:extractDeepLinksDebug': FROM_CACHE, - ':jetpack:featureDebugWriter': SUCCESS, - ':jetpack:generateDebugAssets': UP_TO_DATE, - ':jetpack:generateDebugBuildConfig': FROM_CACHE, - ':jetpack:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':jetpack:generateDebugResValues': FROM_CACHE, - ':jetpack:generateDebugResources': UP_TO_DATE, - ':jetpack:javaPreCompileDebug': FROM_CACHE, - ':jetpack:mergeDebugAssets': FROM_CACHE, - ':jetpack:mergeDebugJavaResource': SUCCESS, - ':jetpack:mergeDebugJniLibFolders': FROM_CACHE, - ':jetpack:mergeDebugNativeLibs': NO_SOURCE, - ':jetpack:mergeDebugResources': SUCCESS, - ':jetpack:mergeDebugShaders': FROM_CACHE, - ':jetpack:mergeExtDexDebug': FROM_CACHE, - ':jetpack:mergeLibDexDebug': FROM_CACHE, - ':jetpack:mergeProjectDexDebug': FROM_CACHE, - ':jetpack:packageDebug': SUCCESS, - ':jetpack:preBuild': UP_TO_DATE, - ':jetpack:preDebugBuild': UP_TO_DATE, - ':jetpack:processApplicationManifestDebugForBundle': FROM_CACHE, - ':jetpack:processDebugJavaRes': NO_SOURCE, - ':jetpack:processDebugMainManifest': FROM_CACHE, - ':jetpack:processDebugManifest': FROM_CACHE, - ':jetpack:processDebugManifestForPackage': FROM_CACHE, - ':jetpack:processDebugResources': SUCCESS, - ':jetpack:processManifestDebugForFeature': FROM_CACHE, - ':jetpack:stripDebugDebugSymbols': NO_SOURCE, - ':memory:assembleDebug': SUCCESS, - ':memory:checkDebugAarMetadata': FROM_CACHE, - ':memory:checkDebugDuplicateClasses': FROM_CACHE, - ':memory:compileDebugAidl': NO_SOURCE, - ':memory:compileDebugJavaWithJavac': FROM_CACHE, - ':memory:compileDebugRenderscript': NO_SOURCE, - ':memory:compileDebugShaders': NO_SOURCE, - ':memory:compressDebugAssets': FROM_CACHE, - ':memory:createDebugApkListingFileRedirect': SUCCESS, - ':memory:createDebugCompatibleScreenManifests': FROM_CACHE, - ':memory:desugarDebugFileDependencies': FROM_CACHE, - ':memory:dexBuilderDebug': FROM_CACHE, - ':memory:extractDeepLinksDebug': FROM_CACHE, - ':memory:featureDebugWriter': SUCCESS, - ':memory:generateDebugAssets': UP_TO_DATE, - ':memory:generateDebugBuildConfig': FROM_CACHE, - ':memory:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':memory:generateDebugResValues': FROM_CACHE, - ':memory:generateDebugResources': UP_TO_DATE, - ':memory:javaPreCompileDebug': FROM_CACHE, - ':memory:mergeDebugAssets': FROM_CACHE, - ':memory:mergeDebugJavaResource': SUCCESS, - ':memory:mergeDebugJniLibFolders': FROM_CACHE, - ':memory:mergeDebugNativeLibs': NO_SOURCE, - ':memory:mergeDebugResources': SUCCESS, - ':memory:mergeDebugShaders': FROM_CACHE, - ':memory:mergeExtDexDebug': FROM_CACHE, - ':memory:mergeLibDexDebug': FROM_CACHE, - ':memory:mergeProjectDexDebug': FROM_CACHE, - ':memory:packageDebug': SUCCESS, - ':memory:preBuild': UP_TO_DATE, - ':memory:preDebugBuild': UP_TO_DATE, - ':memory:processApplicationManifestDebugForBundle': FROM_CACHE, - ':memory:processDebugJavaRes': NO_SOURCE, - ':memory:processDebugMainManifest': FROM_CACHE, - ':memory:processDebugManifest': FROM_CACHE, - ':memory:processDebugManifestForPackage': FROM_CACHE, - ':memory:processDebugResources': SUCCESS, - ':memory:processManifestDebugForFeature': FROM_CACHE, - ':memory:stripDebugDebugSymbols': NO_SOURCE, - ':penguinswim:assembleDebug': SUCCESS, - ':penguinswim:checkDebugAarMetadata': FROM_CACHE, - ':penguinswim:checkDebugDuplicateClasses': FROM_CACHE, - ':penguinswim:compileDebugAidl': NO_SOURCE, - ':penguinswim:compileDebugJavaWithJavac': FROM_CACHE, - ':penguinswim:compileDebugRenderscript': NO_SOURCE, - ':penguinswim:compileDebugShaders': NO_SOURCE, - ':penguinswim:compressDebugAssets': FROM_CACHE, - ':penguinswim:createDebugApkListingFileRedirect': SUCCESS, - ':penguinswim:createDebugCompatibleScreenManifests': FROM_CACHE, - ':penguinswim:desugarDebugFileDependencies': FROM_CACHE, - ':penguinswim:dexBuilderDebug': FROM_CACHE, - ':penguinswim:extractDeepLinksDebug': FROM_CACHE, - ':penguinswim:featureDebugWriter': SUCCESS, - ':penguinswim:generateDebugAssets': UP_TO_DATE, - ':penguinswim:generateDebugBuildConfig': FROM_CACHE, - ':penguinswim:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':penguinswim:generateDebugResValues': FROM_CACHE, - ':penguinswim:generateDebugResources': UP_TO_DATE, - ':penguinswim:javaPreCompileDebug': FROM_CACHE, - ':penguinswim:mergeDebugAssets': FROM_CACHE, - ':penguinswim:mergeDebugJavaResource': SUCCESS, - ':penguinswim:mergeDebugJniLibFolders': FROM_CACHE, - ':penguinswim:mergeDebugNativeLibs': NO_SOURCE, - ':penguinswim:mergeDebugResources': SUCCESS, - ':penguinswim:mergeDebugShaders': FROM_CACHE, - ':penguinswim:mergeExtDexDebug': FROM_CACHE, - ':penguinswim:mergeLibDexDebug': FROM_CACHE, - ':penguinswim:mergeProjectDexDebug': FROM_CACHE, - ':penguinswim:packageDebug': SUCCESS, - ':penguinswim:preBuild': UP_TO_DATE, - ':penguinswim:preDebugBuild': UP_TO_DATE, - ':penguinswim:processApplicationManifestDebugForBundle': FROM_CACHE, - ':penguinswim:processDebugJavaRes': NO_SOURCE, - ':penguinswim:processDebugMainManifest': FROM_CACHE, - ':penguinswim:processDebugManifest': FROM_CACHE, - ':penguinswim:processDebugManifestForPackage': FROM_CACHE, - ':penguinswim:processDebugResources': SUCCESS, - ':penguinswim:processManifestDebugForFeature': FROM_CACHE, - ':penguinswim:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:assembleDebug': SUCCESS, - ':playgames:bundleDebugAar': SUCCESS, - ':playgames:bundleLibCompileToJarDebug': FROM_CACHE, - ':playgames:bundleLibResDebug': NO_SOURCE, - ':playgames:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':playgames:compileDebugAidl': NO_SOURCE, - ':playgames:compileDebugJavaWithJavac': FROM_CACHE, - ':playgames:compileDebugLibraryResources': SUCCESS, - ':playgames:compileDebugRenderscript': NO_SOURCE, - ':playgames:compileDebugShaders': NO_SOURCE, - ':playgames:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':playgames:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':playgames:createFullJarDebug': FROM_CACHE, - ':playgames:extractDebugAnnotations': FROM_CACHE, - ':playgames:extractDeepLinksDebug': FROM_CACHE, - ':playgames:generateDebugAssets': UP_TO_DATE, - ':playgames:generateDebugBuildConfig': FROM_CACHE, - ':playgames:generateDebugRFile': FROM_CACHE, - ':playgames:generateDebugResValues': FROM_CACHE, - ':playgames:generateDebugResources': UP_TO_DATE, - ':playgames:javaPreCompileDebug': FROM_CACHE, - ':playgames:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':playgames:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':playgames:mergeDebugJavaResource': SUCCESS, - ':playgames:mergeDebugJniLibFolders': FROM_CACHE, - ':playgames:mergeDebugNativeLibs': NO_SOURCE, - ':playgames:mergeDebugShaders': FROM_CACHE, - ':playgames:packageDebugAssets': FROM_CACHE, - ':playgames:packageDebugRenderscript': NO_SOURCE, - ':playgames:packageDebugResources': FROM_CACHE, - ':playgames:parseDebugLocalResources': FROM_CACHE, - ':playgames:preBuild': UP_TO_DATE, - ':playgames:preDebugBuild': UP_TO_DATE, - ':playgames:prepareDebugArtProfile': SUCCESS, - ':playgames:prepareLintJarForPublish': SUCCESS, - ':playgames:processDebugJavaRes': NO_SOURCE, - ':playgames:processDebugManifest': FROM_CACHE, - ':playgames:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:syncDebugLibJars': FROM_CACHE, - ':playgames:writeDebugAarMetadata': FROM_CACHE, - ':presenttoss:assembleDebug': SUCCESS, - ':presenttoss:checkDebugAarMetadata': FROM_CACHE, - ':presenttoss:checkDebugDuplicateClasses': FROM_CACHE, - ':presenttoss:compileDebugAidl': NO_SOURCE, - ':presenttoss:compileDebugJavaWithJavac': FROM_CACHE, - ':presenttoss:compileDebugRenderscript': NO_SOURCE, - ':presenttoss:compileDebugShaders': NO_SOURCE, - ':presenttoss:compressDebugAssets': FROM_CACHE, - ':presenttoss:createDebugApkListingFileRedirect': SUCCESS, - ':presenttoss:createDebugCompatibleScreenManifests': FROM_CACHE, - ':presenttoss:desugarDebugFileDependencies': FROM_CACHE, - ':presenttoss:dexBuilderDebug': FROM_CACHE, - ':presenttoss:extractDeepLinksDebug': FROM_CACHE, - ':presenttoss:featureDebugWriter': SUCCESS, - ':presenttoss:generateDebugAssets': UP_TO_DATE, - ':presenttoss:generateDebugBuildConfig': FROM_CACHE, - ':presenttoss:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':presenttoss:generateDebugResValues': FROM_CACHE, - ':presenttoss:generateDebugResources': UP_TO_DATE, - ':presenttoss:javaPreCompileDebug': FROM_CACHE, - ':presenttoss:mergeDebugAssets': FROM_CACHE, - ':presenttoss:mergeDebugJavaResource': SUCCESS, - ':presenttoss:mergeDebugJniLibFolders': FROM_CACHE, - ':presenttoss:mergeDebugNativeLibs': NO_SOURCE, - ':presenttoss:mergeDebugResources': SUCCESS, - ':presenttoss:mergeDebugShaders': FROM_CACHE, - ':presenttoss:mergeExtDexDebug': FROM_CACHE, - ':presenttoss:mergeLibDexDebug': FROM_CACHE, - ':presenttoss:mergeProjectDexDebug': FROM_CACHE, - ':presenttoss:packageDebug': SUCCESS, - ':presenttoss:preBuild': UP_TO_DATE, - ':presenttoss:preDebugBuild': UP_TO_DATE, - ':presenttoss:processApplicationManifestDebugForBundle': FROM_CACHE, - ':presenttoss:processDebugJavaRes': NO_SOURCE, - ':presenttoss:processDebugMainManifest': FROM_CACHE, - ':presenttoss:processDebugManifest': FROM_CACHE, - ':presenttoss:processDebugManifestForPackage': FROM_CACHE, - ':presenttoss:processDebugResources': SUCCESS, - ':presenttoss:processManifestDebugForFeature': FROM_CACHE, - ':presenttoss:stripDebugDebugSymbols': NO_SOURCE, - ':rocketsleigh:assembleDebug': SUCCESS, - ':rocketsleigh:checkDebugAarMetadata': FROM_CACHE, - ':rocketsleigh:checkDebugDuplicateClasses': FROM_CACHE, - ':rocketsleigh:compileDebugAidl': NO_SOURCE, - ':rocketsleigh:compileDebugJavaWithJavac': FROM_CACHE, - ':rocketsleigh:compileDebugKotlin': FROM_CACHE, - ':rocketsleigh:compileDebugRenderscript': NO_SOURCE, - ':rocketsleigh:compileDebugShaders': NO_SOURCE, - ':rocketsleigh:compressDebugAssets': FROM_CACHE, - ':rocketsleigh:createDebugApkListingFileRedirect': SUCCESS, - ':rocketsleigh:createDebugCompatibleScreenManifests': FROM_CACHE, - ':rocketsleigh:desugarDebugFileDependencies': FROM_CACHE, - ':rocketsleigh:dexBuilderDebug': FROM_CACHE, - ':rocketsleigh:extractDeepLinksDebug': FROM_CACHE, - ':rocketsleigh:featureDebugWriter': SUCCESS, - ':rocketsleigh:generateDebugAssets': UP_TO_DATE, - ':rocketsleigh:generateDebugBuildConfig': FROM_CACHE, - ':rocketsleigh:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':rocketsleigh:generateDebugResValues': FROM_CACHE, - ':rocketsleigh:generateDebugResources': UP_TO_DATE, - ':rocketsleigh:javaPreCompileDebug': FROM_CACHE, - ':rocketsleigh:mergeDebugAssets': FROM_CACHE, - ':rocketsleigh:mergeDebugJavaResource': SUCCESS, - ':rocketsleigh:mergeDebugJniLibFolders': FROM_CACHE, - ':rocketsleigh:mergeDebugNativeLibs': NO_SOURCE, - ':rocketsleigh:mergeDebugResources': SUCCESS, - ':rocketsleigh:mergeDebugShaders': FROM_CACHE, - ':rocketsleigh:mergeExtDexDebug': FROM_CACHE, - ':rocketsleigh:mergeLibDexDebug': FROM_CACHE, - ':rocketsleigh:mergeProjectDexDebug': FROM_CACHE, - ':rocketsleigh:packageDebug': SUCCESS, - ':rocketsleigh:preBuild': UP_TO_DATE, - ':rocketsleigh:preDebugBuild': UP_TO_DATE, - ':rocketsleigh:processApplicationManifestDebugForBundle': FROM_CACHE, - ':rocketsleigh:processDebugJavaRes': NO_SOURCE, - ':rocketsleigh:processDebugMainManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifestForPackage': FROM_CACHE, - ':rocketsleigh:processDebugResources': SUCCESS, - ':rocketsleigh:processManifestDebugForFeature': FROM_CACHE, - ':rocketsleigh:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:assembleDebug': SUCCESS, - ':santa-tracker:bundleDebugClasses': FROM_CACHE, - ':santa-tracker:bundleDebugClassesToRuntimeJar': FROM_CACHE, - ':santa-tracker:checkDebugAarMetadata': FROM_CACHE, - ':santa-tracker:checkDebugDuplicateClasses': FROM_CACHE, - ':santa-tracker:checkDebugLibraries': FROM_CACHE, - ':santa-tracker:compileDebugAidl': NO_SOURCE, - ':santa-tracker:compileDebugJavaWithJavac': FROM_CACHE, - ':santa-tracker:compileDebugKotlin': FROM_CACHE, - ':santa-tracker:compileDebugRenderscript': NO_SOURCE, - ':santa-tracker:compileDebugShaders': NO_SOURCE, - ':santa-tracker:compressDebugAssets': FROM_CACHE, - ':santa-tracker:createDebugApkListingFileRedirect': SUCCESS, - ':santa-tracker:createDebugCompatibleScreenManifests': FROM_CACHE, - ':santa-tracker:desugarDebugFileDependencies': FROM_CACHE, - ':santa-tracker:dexBuilderDebug': FROM_CACHE, - ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, - ':santa-tracker:generateDebugAssets': UP_TO_DATE, - ':santa-tracker:generateDebugBuildConfig': FROM_CACHE, - ':santa-tracker:generateDebugFeatureMetadata': FROM_CACHE, - ':santa-tracker:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':santa-tracker:generateDebugResValues': FROM_CACHE, - ':santa-tracker:generateDebugResources': UP_TO_DATE, - ':santa-tracker:handleDebugMicroApk': FROM_CACHE, - ':santa-tracker:javaPreCompileDebug': FROM_CACHE, - ':santa-tracker:kaptDebugKotlin': FROM_CACHE, - ':santa-tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':santa-tracker:mergeDebugAssets': FROM_CACHE, - ':santa-tracker:mergeDebugJavaResource': SUCCESS, - ':santa-tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':santa-tracker:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':santa-tracker:mergeDebugNativeLibs': NO_SOURCE, - ':santa-tracker:mergeDebugResources': SUCCESS, - ':santa-tracker:mergeDebugShaders': FROM_CACHE, - ':santa-tracker:mergeExtDexDebug': FROM_CACHE, - ':santa-tracker:mergeLibDexDebug': FROM_CACHE, - ':santa-tracker:mergeProjectDexDebug': FROM_CACHE, - ':santa-tracker:packageDebug': SUCCESS, - ':santa-tracker:preBuild': UP_TO_DATE, - ':santa-tracker:preDebugBuild': FROM_CACHE, - ':santa-tracker:processDebugJavaRes': NO_SOURCE, - ':santa-tracker:processDebugMainManifest': FROM_CACHE, - ':santa-tracker:processDebugManifest': FROM_CACHE, - ':santa-tracker:processDebugManifestForPackage': FROM_CACHE, - ':santa-tracker:processDebugResources': SUCCESS, - ':santa-tracker:signingConfigWriterDebug': FROM_CACHE, - ':santa-tracker:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:validateSigningDebug': FROM_CACHE, - ':santa-tracker:writeDebugAppMetadata': FROM_CACHE, - ':santa-tracker:writeDebugModuleMetadata': SUCCESS, - ':santa-tracker:writeDebugSigningConfigVersions': FROM_CACHE, - ':snowballrun:assembleDebug': SUCCESS, - ':snowballrun:checkDebugAarMetadata': FROM_CACHE, - ':snowballrun:checkDebugDuplicateClasses': FROM_CACHE, - ':snowballrun:compileDebugAidl': NO_SOURCE, - ':snowballrun:compileDebugJavaWithJavac': FROM_CACHE, - ':snowballrun:compileDebugRenderscript': NO_SOURCE, - ':snowballrun:compileDebugShaders': NO_SOURCE, - ':snowballrun:compressDebugAssets': FROM_CACHE, - ':snowballrun:createDebugApkListingFileRedirect': SUCCESS, - ':snowballrun:createDebugCompatibleScreenManifests': FROM_CACHE, - ':snowballrun:desugarDebugFileDependencies': FROM_CACHE, - ':snowballrun:dexBuilderDebug': FROM_CACHE, - ':snowballrun:extractDeepLinksDebug': FROM_CACHE, - ':snowballrun:featureDebugWriter': SUCCESS, - ':snowballrun:generateDebugAssets': UP_TO_DATE, - ':snowballrun:generateDebugBuildConfig': FROM_CACHE, - ':snowballrun:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':snowballrun:generateDebugResValues': FROM_CACHE, - ':snowballrun:generateDebugResources': UP_TO_DATE, - ':snowballrun:javaPreCompileDebug': FROM_CACHE, - ':snowballrun:mergeDebugAssets': FROM_CACHE, - ':snowballrun:mergeDebugJavaResource': SUCCESS, - ':snowballrun:mergeDebugJniLibFolders': FROM_CACHE, - ':snowballrun:mergeDebugNativeLibs': NO_SOURCE, - ':snowballrun:mergeDebugResources': SUCCESS, - ':snowballrun:mergeDebugShaders': FROM_CACHE, - ':snowballrun:mergeExtDexDebug': FROM_CACHE, - ':snowballrun:mergeLibDexDebug': FROM_CACHE, - ':snowballrun:mergeProjectDexDebug': FROM_CACHE, - ':snowballrun:packageDebug': SUCCESS, - ':snowballrun:preBuild': UP_TO_DATE, - ':snowballrun:preDebugBuild': UP_TO_DATE, - ':snowballrun:processApplicationManifestDebugForBundle': FROM_CACHE, - ':snowballrun:processDebugJavaRes': NO_SOURCE, - ':snowballrun:processDebugMainManifest': FROM_CACHE, - ':snowballrun:processDebugManifest': FROM_CACHE, - ':snowballrun:processDebugManifestForPackage': FROM_CACHE, - ':snowballrun:processDebugResources': SUCCESS, - ':snowballrun:processManifestDebugForFeature': FROM_CACHE, - ':snowballrun:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:assembleDebug': SUCCESS, - ':tracker:bundleDebugAar': SUCCESS, - ':tracker:bundleLibCompileToJarDebug': FROM_CACHE, - ':tracker:bundleLibResDebug': FROM_CACHE, - ':tracker:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':tracker:compileDebugAidl': NO_SOURCE, - ':tracker:compileDebugJavaWithJavac': SUCCESS, - ':tracker:compileDebugKotlin': FROM_CACHE, - ':tracker:compileDebugLibraryResources': SUCCESS, - ':tracker:compileDebugRenderscript': NO_SOURCE, - ':tracker:compileDebugShaders': NO_SOURCE, - ':tracker:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':tracker:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':tracker:createFullJarDebug': FROM_CACHE, - ':tracker:extractDebugAnnotations': FROM_CACHE, - ':tracker:extractDeepLinksDebug': FROM_CACHE, - ':tracker:generateDebugAssets': UP_TO_DATE, - ':tracker:generateDebugBuildConfig': FROM_CACHE, - ':tracker:generateDebugRFile': FROM_CACHE, - ':tracker:generateDebugResValues': FROM_CACHE, - ':tracker:generateDebugResources': UP_TO_DATE, - ':tracker:javaPreCompileDebug': FROM_CACHE, - ':tracker:kaptDebugKotlin': FROM_CACHE, - ':tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':tracker:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':tracker:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':tracker:mergeDebugJavaResource': SUCCESS, - ':tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':tracker:mergeDebugNativeLibs': NO_SOURCE, - ':tracker:mergeDebugShaders': FROM_CACHE, - ':tracker:packageDebugAssets': FROM_CACHE, - ':tracker:packageDebugRenderscript': NO_SOURCE, - ':tracker:packageDebugResources': FROM_CACHE, - ':tracker:parseDebugLocalResources': FROM_CACHE, - ':tracker:preBuild': UP_TO_DATE, - ':tracker:preDebugBuild': UP_TO_DATE, - ':tracker:prepareDebugArtProfile': SUCCESS, - ':tracker:prepareLintJarForPublish': SUCCESS, - ':tracker:processDebugJavaRes': NO_SOURCE, - ':tracker:processDebugManifest': FROM_CACHE, - ':tracker:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:syncDebugLibJars': FROM_CACHE, - ':tracker:writeDebugAarMetadata': FROM_CACHE, - ':wearable:assembleDebug': SUCCESS, - ':wearable:checkDebugAarMetadata': FROM_CACHE, - ':wearable:checkDebugDuplicateClasses': FROM_CACHE, - ':wearable:compileDebugAidl': NO_SOURCE, - ':wearable:compileDebugJavaWithJavac': FROM_CACHE, - ':wearable:compileDebugKotlin': FROM_CACHE, - ':wearable:compileDebugRenderscript': NO_SOURCE, - ':wearable:compileDebugShaders': NO_SOURCE, - ':wearable:compressDebugAssets': FROM_CACHE, - ':wearable:createDebugApkListingFileRedirect': SUCCESS, - ':wearable:createDebugCompatibleScreenManifests': FROM_CACHE, - ':wearable:desugarDebugFileDependencies': FROM_CACHE, - ':wearable:dexBuilderDebug': FROM_CACHE, - ':wearable:extractDeepLinksDebug': FROM_CACHE, - ':wearable:generateDebugAssets': UP_TO_DATE, - ':wearable:generateDebugBuildConfig': FROM_CACHE, - ':wearable:generateDebugResValues': FROM_CACHE, - ':wearable:generateDebugResources': UP_TO_DATE, - ':wearable:javaPreCompileDebug': FROM_CACHE, - ':wearable:kaptDebugKotlin': SKIPPED, - ':wearable:kaptGenerateStubsDebugKotlin': SKIPPED, - ':wearable:mergeDebugAssets': FROM_CACHE, - ':wearable:mergeDebugJavaResource': SUCCESS, - ':wearable:mergeDebugJniLibFolders': FROM_CACHE, - ':wearable:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':wearable:mergeDebugNativeLibs': NO_SOURCE, - ':wearable:mergeDebugResources': SUCCESS, - ':wearable:mergeDebugShaders': FROM_CACHE, - ':wearable:mergeExtDexDebug': FROM_CACHE, - ':wearable:mergeLibDexDebug': FROM_CACHE, - ':wearable:mergeProjectDexDebug': FROM_CACHE, - ':wearable:packageDebug': SUCCESS, - ':wearable:preBuild': UP_TO_DATE, - ':wearable:preDebugBuild': UP_TO_DATE, - ':wearable:processDebugJavaRes': NO_SOURCE, - ':wearable:processDebugMainManifest': FROM_CACHE, - ':wearable:processDebugManifest': FROM_CACHE, - ':wearable:processDebugManifestForPackage': FROM_CACHE, - ':wearable:processDebugResources': SUCCESS, - ':wearable:stripDebugDebugSymbols': NO_SOURCE, - ':wearable:validateSigningDebug': FROM_CACHE, - ':wearable:writeDebugAppMetadata': FROM_CACHE, - ':wearable:writeDebugSigningConfigVersions': FROM_CACHE, - ] -} - -class AndroidPluginExpectations70 { - - static final EXPECTED_RESULTS_7_0 = [ - ':cityquiz:assembleDebug': SUCCESS, - ':cityquiz:checkDebugAarMetadata': FROM_CACHE, - ':cityquiz:checkDebugDuplicateClasses': FROM_CACHE, - ':cityquiz:compileDebugAidl': NO_SOURCE, - ':cityquiz:compileDebugJavaWithJavac': FROM_CACHE, - ':cityquiz:compileDebugKotlin': FROM_CACHE, - ':cityquiz:compileDebugRenderscript': NO_SOURCE, - ':cityquiz:compileDebugShaders': NO_SOURCE, - ':cityquiz:compileDebugSources': UP_TO_DATE, - ':cityquiz:compressDebugAssets': FROM_CACHE, - ':cityquiz:createDebugCompatibleScreenManifests': FROM_CACHE, - ':cityquiz:desugarDebugFileDependencies': FROM_CACHE, - ':cityquiz:dexBuilderDebug': FROM_CACHE, - ':cityquiz:extractDeepLinksDebug': FROM_CACHE, - ':cityquiz:featureDebugWriter': SUCCESS, - ':cityquiz:generateDebugAssets': UP_TO_DATE, - ':cityquiz:generateDebugBuildConfig': FROM_CACHE, - ':cityquiz:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':cityquiz:generateDebugResValues': FROM_CACHE, - ':cityquiz:generateDebugResources': UP_TO_DATE, - ':cityquiz:javaPreCompileDebug': FROM_CACHE, - ':cityquiz:mergeDebugAssets': FROM_CACHE, - ':cityquiz:mergeDebugJavaResource': SUCCESS, - ':cityquiz:mergeDebugJniLibFolders': FROM_CACHE, - ':cityquiz:mergeDebugNativeLibs': NO_SOURCE, - ':cityquiz:mergeDebugResources': SUCCESS, - ':cityquiz:mergeDebugShaders': FROM_CACHE, - ':cityquiz:mergeExtDexDebug': FROM_CACHE, - ':cityquiz:mergeLibDexDebug': FROM_CACHE, - ':cityquiz:mergeProjectDexDebug': FROM_CACHE, - ':cityquiz:packageDebug': SUCCESS, - ':cityquiz:preBuild': UP_TO_DATE, - ':cityquiz:preDebugBuild': UP_TO_DATE, - ':cityquiz:processApplicationManifestDebugForBundle': FROM_CACHE, - ':cityquiz:processDebugJavaRes': NO_SOURCE, - ':cityquiz:processDebugMainManifest': FROM_CACHE, - ':cityquiz:processDebugManifest': FROM_CACHE, - ':cityquiz:processDebugManifestForPackage': FROM_CACHE, - ':cityquiz:processDebugResources': SUCCESS, - ':cityquiz:processManifestDebugForFeature': FROM_CACHE, - ':cityquiz:stripDebugDebugSymbols': NO_SOURCE, - ':common:assembleDebug': SUCCESS, - ':common:bundleDebugAar': SUCCESS, - ':common:bundleLibCompileToJarDebug': FROM_CACHE, - ':common:bundleLibResDebug': FROM_CACHE, - ':common:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':common:compileDebugAidl': NO_SOURCE, - ':common:compileDebugJavaWithJavac': FROM_CACHE, - ':common:compileDebugKotlin': FROM_CACHE, - ':common:compileDebugLibraryResources': SUCCESS, - ':common:compileDebugRenderscript': NO_SOURCE, - ':common:compileDebugShaders': NO_SOURCE, - ':common:compileDebugSources': UP_TO_DATE, - ':common:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':common:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':common:createFullJarDebug': FROM_CACHE, - ':common:extractDebugAnnotations': FROM_CACHE, - ':common:extractDeepLinksDebug': FROM_CACHE, - ':common:generateDebugAssets': UP_TO_DATE, - ':common:generateDebugBuildConfig': FROM_CACHE, - ':common:generateDebugRFile': FROM_CACHE, - ':common:generateDebugResValues': FROM_CACHE, - ':common:generateDebugResources': UP_TO_DATE, - ':common:javaPreCompileDebug': FROM_CACHE, - ':common:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':common:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':common:mergeDebugJavaResource': SUCCESS, - ':common:mergeDebugJniLibFolders': FROM_CACHE, - ':common:mergeDebugNativeLibs': NO_SOURCE, - ':common:mergeDebugShaders': FROM_CACHE, - ':common:packageDebugAssets': FROM_CACHE, - ':common:packageDebugRenderscript': NO_SOURCE, - ':common:packageDebugResources': FROM_CACHE, - ':common:parseDebugLocalResources': FROM_CACHE, - ':common:preBuild': UP_TO_DATE, - ':common:preDebugBuild': UP_TO_DATE, - ':common:prepareDebugArtProfile': SUCCESS, - ':common:prepareLintJarForPublish': SUCCESS, - ':common:processDebugJavaRes': NO_SOURCE, - ':common:processDebugManifest': FROM_CACHE, - ':common:stripDebugDebugSymbols': NO_SOURCE, - ':common:syncDebugLibJars': FROM_CACHE, - ':common:writeDebugAarMetadata': FROM_CACHE, - ':dasherdancer:assembleDebug': SUCCESS, - ':dasherdancer:checkDebugAarMetadata': FROM_CACHE, - ':dasherdancer:checkDebugDuplicateClasses': FROM_CACHE, - ':dasherdancer:compileDebugAidl': NO_SOURCE, - ':dasherdancer:compileDebugJavaWithJavac': FROM_CACHE, - ':dasherdancer:compileDebugKotlin': FROM_CACHE, - ':dasherdancer:compileDebugRenderscript': NO_SOURCE, - ':dasherdancer:compileDebugShaders': NO_SOURCE, - ':dasherdancer:compileDebugSources': UP_TO_DATE, - ':dasherdancer:compressDebugAssets': FROM_CACHE, - ':dasherdancer:createDebugCompatibleScreenManifests': FROM_CACHE, - ':dasherdancer:desugarDebugFileDependencies': FROM_CACHE, - ':dasherdancer:dexBuilderDebug': FROM_CACHE, - ':dasherdancer:extractDeepLinksDebug': FROM_CACHE, - ':dasherdancer:featureDebugWriter': SUCCESS, - ':dasherdancer:generateDebugAssets': UP_TO_DATE, - ':dasherdancer:generateDebugBuildConfig': FROM_CACHE, - ':dasherdancer:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':dasherdancer:generateDebugResValues': FROM_CACHE, - ':dasherdancer:generateDebugResources': UP_TO_DATE, - ':dasherdancer:javaPreCompileDebug': FROM_CACHE, - ':dasherdancer:mergeDebugAssets': FROM_CACHE, - ':dasherdancer:mergeDebugJavaResource': SUCCESS, - ':dasherdancer:mergeDebugJniLibFolders': FROM_CACHE, - ':dasherdancer:mergeDebugNativeLibs': NO_SOURCE, - ':dasherdancer:mergeDebugResources': SUCCESS, - ':dasherdancer:mergeDebugShaders': FROM_CACHE, - ':dasherdancer:mergeExtDexDebug': FROM_CACHE, - ':dasherdancer:mergeLibDexDebug': FROM_CACHE, - ':dasherdancer:mergeProjectDexDebug': FROM_CACHE, - ':dasherdancer:packageDebug': SUCCESS, - ':dasherdancer:preBuild': UP_TO_DATE, - ':dasherdancer:preDebugBuild': UP_TO_DATE, - ':dasherdancer:processApplicationManifestDebugForBundle': FROM_CACHE, - ':dasherdancer:processDebugJavaRes': NO_SOURCE, - ':dasherdancer:processDebugMainManifest': FROM_CACHE, - ':dasherdancer:processDebugManifest': FROM_CACHE, - ':dasherdancer:processDebugManifestForPackage': FROM_CACHE, - ':dasherdancer:processDebugResources': SUCCESS, - ':dasherdancer:processManifestDebugForFeature': FROM_CACHE, - ':dasherdancer:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:assembleDebug': SUCCESS, - ':doodles-lib:bundleDebugAar': SUCCESS, - ':doodles-lib:bundleLibCompileToJarDebug': FROM_CACHE, - ':doodles-lib:bundleLibResDebug': NO_SOURCE, - ':doodles-lib:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':doodles-lib:compileDebugAidl': NO_SOURCE, - ':doodles-lib:compileDebugJavaWithJavac': FROM_CACHE, - ':doodles-lib:compileDebugLibraryResources': SUCCESS, - ':doodles-lib:compileDebugRenderscript': NO_SOURCE, - ':doodles-lib:compileDebugShaders': NO_SOURCE, - ':doodles-lib:compileDebugSources': UP_TO_DATE, - ':doodles-lib:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':doodles-lib:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':doodles-lib:createFullJarDebug': FROM_CACHE, - ':doodles-lib:extractDebugAnnotations': FROM_CACHE, - ':doodles-lib:extractDeepLinksDebug': FROM_CACHE, - ':doodles-lib:generateDebugAssets': UP_TO_DATE, - ':doodles-lib:generateDebugBuildConfig': FROM_CACHE, - ':doodles-lib:generateDebugRFile': FROM_CACHE, - ':doodles-lib:generateDebugResValues': FROM_CACHE, - ':doodles-lib:generateDebugResources': UP_TO_DATE, - ':doodles-lib:javaPreCompileDebug': FROM_CACHE, - ':doodles-lib:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugJavaResource': SUCCESS, - ':doodles-lib:mergeDebugJniLibFolders': FROM_CACHE, - ':doodles-lib:mergeDebugNativeLibs': NO_SOURCE, - ':doodles-lib:mergeDebugShaders': FROM_CACHE, - ':doodles-lib:packageDebugAssets': FROM_CACHE, - ':doodles-lib:packageDebugRenderscript': NO_SOURCE, - ':doodles-lib:packageDebugResources': FROM_CACHE, - ':doodles-lib:parseDebugLocalResources': FROM_CACHE, - ':doodles-lib:preBuild': UP_TO_DATE, - ':doodles-lib:preDebugBuild': UP_TO_DATE, - ':doodles-lib:prepareDebugArtProfile': SUCCESS, - ':doodles-lib:prepareLintJarForPublish': SUCCESS, - ':doodles-lib:processDebugJavaRes': NO_SOURCE, - ':doodles-lib:processDebugManifest': FROM_CACHE, - ':doodles-lib:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:syncDebugLibJars': FROM_CACHE, - ':doodles-lib:writeDebugAarMetadata': FROM_CACHE, - ':gumball:assembleDebug': SUCCESS, - ':gumball:checkDebugAarMetadata': FROM_CACHE, - ':gumball:checkDebugDuplicateClasses': FROM_CACHE, - ':gumball:compileDebugAidl': NO_SOURCE, - ':gumball:compileDebugJavaWithJavac': FROM_CACHE, - ':gumball:compileDebugRenderscript': NO_SOURCE, - ':gumball:compileDebugShaders': NO_SOURCE, - ':gumball:compileDebugSources': UP_TO_DATE, - ':gumball:compressDebugAssets': FROM_CACHE, - ':gumball:createDebugCompatibleScreenManifests': FROM_CACHE, - ':gumball:desugarDebugFileDependencies': FROM_CACHE, - ':gumball:dexBuilderDebug': FROM_CACHE, - ':gumball:extractDeepLinksDebug': FROM_CACHE, - ':gumball:featureDebugWriter': SUCCESS, - ':gumball:generateDebugAssets': UP_TO_DATE, - ':gumball:generateDebugBuildConfig': FROM_CACHE, - ':gumball:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':gumball:generateDebugResValues': FROM_CACHE, - ':gumball:generateDebugResources': UP_TO_DATE, - ':gumball:javaPreCompileDebug': FROM_CACHE, - ':gumball:mergeDebugAssets': FROM_CACHE, - ':gumball:mergeDebugJavaResource': SUCCESS, - ':gumball:mergeDebugJniLibFolders': FROM_CACHE, - ':gumball:mergeDebugNativeLibs': NO_SOURCE, - ':gumball:mergeDebugResources': SUCCESS, - ':gumball:mergeDebugShaders': FROM_CACHE, - ':gumball:mergeExtDexDebug': FROM_CACHE, - ':gumball:mergeLibDexDebug': FROM_CACHE, - ':gumball:mergeProjectDexDebug': FROM_CACHE, - ':gumball:packageDebug': SUCCESS, - ':gumball:preBuild': UP_TO_DATE, - ':gumball:preDebugBuild': UP_TO_DATE, - ':gumball:processApplicationManifestDebugForBundle': FROM_CACHE, - ':gumball:processDebugJavaRes': NO_SOURCE, - ':gumball:processDebugMainManifest': FROM_CACHE, - ':gumball:processDebugManifest': FROM_CACHE, - ':gumball:processDebugManifestForPackage': FROM_CACHE, - ':gumball:processDebugResources': SUCCESS, - ':gumball:processManifestDebugForFeature': FROM_CACHE, - ':gumball:stripDebugDebugSymbols': NO_SOURCE, - ':jetpack:assembleDebug': SUCCESS, - ':jetpack:checkDebugAarMetadata': FROM_CACHE, - ':jetpack:checkDebugDuplicateClasses': FROM_CACHE, - ':jetpack:compileDebugAidl': NO_SOURCE, - ':jetpack:compileDebugJavaWithJavac': FROM_CACHE, - ':jetpack:compileDebugKotlin': FROM_CACHE, - ':jetpack:compileDebugRenderscript': NO_SOURCE, - ':jetpack:compileDebugShaders': NO_SOURCE, - ':jetpack:compileDebugSources': UP_TO_DATE, - ':jetpack:compressDebugAssets': FROM_CACHE, - ':jetpack:createDebugCompatibleScreenManifests': FROM_CACHE, - ':jetpack:desugarDebugFileDependencies': FROM_CACHE, - ':jetpack:dexBuilderDebug': FROM_CACHE, - ':jetpack:extractDeepLinksDebug': FROM_CACHE, - ':jetpack:featureDebugWriter': SUCCESS, - ':jetpack:generateDebugAssets': UP_TO_DATE, - ':jetpack:generateDebugBuildConfig': FROM_CACHE, - ':jetpack:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':jetpack:generateDebugResValues': FROM_CACHE, - ':jetpack:generateDebugResources': UP_TO_DATE, - ':jetpack:javaPreCompileDebug': FROM_CACHE, - ':jetpack:mergeDebugAssets': FROM_CACHE, - ':jetpack:mergeDebugJavaResource': SUCCESS, - ':jetpack:mergeDebugJniLibFolders': FROM_CACHE, - ':jetpack:mergeDebugNativeLibs': NO_SOURCE, - ':jetpack:mergeDebugResources': SUCCESS, - ':jetpack:mergeDebugShaders': FROM_CACHE, - ':jetpack:mergeExtDexDebug': FROM_CACHE, - ':jetpack:mergeLibDexDebug': FROM_CACHE, - ':jetpack:mergeProjectDexDebug': FROM_CACHE, - ':jetpack:packageDebug': SUCCESS, - ':jetpack:preBuild': UP_TO_DATE, - ':jetpack:preDebugBuild': UP_TO_DATE, - ':jetpack:processApplicationManifestDebugForBundle': FROM_CACHE, - ':jetpack:processDebugJavaRes': NO_SOURCE, - ':jetpack:processDebugMainManifest': FROM_CACHE, - ':jetpack:processDebugManifest': FROM_CACHE, - ':jetpack:processDebugManifestForPackage': FROM_CACHE, - ':jetpack:processDebugResources': SUCCESS, - ':jetpack:processManifestDebugForFeature': FROM_CACHE, - ':jetpack:stripDebugDebugSymbols': NO_SOURCE, - ':memory:assembleDebug': SUCCESS, - ':memory:checkDebugAarMetadata': FROM_CACHE, - ':memory:checkDebugDuplicateClasses': FROM_CACHE, - ':memory:compileDebugAidl': NO_SOURCE, - ':memory:compileDebugJavaWithJavac': FROM_CACHE, - ':memory:compileDebugRenderscript': NO_SOURCE, - ':memory:compileDebugShaders': NO_SOURCE, - ':memory:compileDebugSources': UP_TO_DATE, - ':memory:compressDebugAssets': FROM_CACHE, - ':memory:createDebugCompatibleScreenManifests': FROM_CACHE, - ':memory:desugarDebugFileDependencies': FROM_CACHE, - ':memory:dexBuilderDebug': FROM_CACHE, - ':memory:extractDeepLinksDebug': FROM_CACHE, - ':memory:featureDebugWriter': SUCCESS, - ':memory:generateDebugAssets': UP_TO_DATE, - ':memory:generateDebugBuildConfig': FROM_CACHE, - ':memory:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':memory:generateDebugResValues': FROM_CACHE, - ':memory:generateDebugResources': UP_TO_DATE, - ':memory:javaPreCompileDebug': FROM_CACHE, - ':memory:mergeDebugAssets': FROM_CACHE, - ':memory:mergeDebugJavaResource': SUCCESS, - ':memory:mergeDebugJniLibFolders': FROM_CACHE, - ':memory:mergeDebugNativeLibs': NO_SOURCE, - ':memory:mergeDebugResources': SUCCESS, - ':memory:mergeDebugShaders': FROM_CACHE, - ':memory:mergeExtDexDebug': FROM_CACHE, - ':memory:mergeLibDexDebug': FROM_CACHE, - ':memory:mergeProjectDexDebug': FROM_CACHE, - ':memory:packageDebug': SUCCESS, - ':memory:preBuild': UP_TO_DATE, - ':memory:preDebugBuild': UP_TO_DATE, - ':memory:processApplicationManifestDebugForBundle': FROM_CACHE, - ':memory:processDebugJavaRes': NO_SOURCE, - ':memory:processDebugMainManifest': FROM_CACHE, - ':memory:processDebugManifest': FROM_CACHE, - ':memory:processDebugManifestForPackage': FROM_CACHE, - ':memory:processDebugResources': SUCCESS, - ':memory:processManifestDebugForFeature': FROM_CACHE, - ':memory:stripDebugDebugSymbols': NO_SOURCE, - ':penguinswim:assembleDebug': SUCCESS, - ':penguinswim:checkDebugAarMetadata': FROM_CACHE, - ':penguinswim:checkDebugDuplicateClasses': FROM_CACHE, - ':penguinswim:compileDebugAidl': NO_SOURCE, - ':penguinswim:compileDebugJavaWithJavac': FROM_CACHE, - ':penguinswim:compileDebugRenderscript': NO_SOURCE, - ':penguinswim:compileDebugShaders': NO_SOURCE, - ':penguinswim:compileDebugSources': UP_TO_DATE, - ':penguinswim:compressDebugAssets': FROM_CACHE, - ':penguinswim:createDebugCompatibleScreenManifests': FROM_CACHE, - ':penguinswim:desugarDebugFileDependencies': FROM_CACHE, - ':penguinswim:dexBuilderDebug': FROM_CACHE, - ':penguinswim:extractDeepLinksDebug': FROM_CACHE, - ':penguinswim:featureDebugWriter': SUCCESS, - ':penguinswim:generateDebugAssets': UP_TO_DATE, - ':penguinswim:generateDebugBuildConfig': FROM_CACHE, - ':penguinswim:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':penguinswim:generateDebugResValues': FROM_CACHE, - ':penguinswim:generateDebugResources': UP_TO_DATE, - ':penguinswim:javaPreCompileDebug': FROM_CACHE, - ':penguinswim:mergeDebugAssets': FROM_CACHE, - ':penguinswim:mergeDebugJavaResource': SUCCESS, - ':penguinswim:mergeDebugJniLibFolders': FROM_CACHE, - ':penguinswim:mergeDebugNativeLibs': NO_SOURCE, - ':penguinswim:mergeDebugResources': SUCCESS, - ':penguinswim:mergeDebugShaders': FROM_CACHE, - ':penguinswim:mergeExtDexDebug': FROM_CACHE, - ':penguinswim:mergeLibDexDebug': FROM_CACHE, - ':penguinswim:mergeProjectDexDebug': FROM_CACHE, - ':penguinswim:packageDebug': SUCCESS, - ':penguinswim:preBuild': UP_TO_DATE, - ':penguinswim:preDebugBuild': UP_TO_DATE, - ':penguinswim:processApplicationManifestDebugForBundle': FROM_CACHE, - ':penguinswim:processDebugJavaRes': NO_SOURCE, - ':penguinswim:processDebugMainManifest': FROM_CACHE, - ':penguinswim:processDebugManifest': FROM_CACHE, - ':penguinswim:processDebugManifestForPackage': FROM_CACHE, - ':penguinswim:processDebugResources': SUCCESS, - ':penguinswim:processManifestDebugForFeature': FROM_CACHE, - ':penguinswim:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:assembleDebug': SUCCESS, - ':playgames:bundleDebugAar': SUCCESS, - ':playgames:bundleLibCompileToJarDebug': FROM_CACHE, - ':playgames:bundleLibResDebug': NO_SOURCE, - ':playgames:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':playgames:compileDebugAidl': NO_SOURCE, - ':playgames:compileDebugJavaWithJavac': FROM_CACHE, - ':playgames:compileDebugLibraryResources': SUCCESS, - ':playgames:compileDebugRenderscript': NO_SOURCE, - ':playgames:compileDebugShaders': NO_SOURCE, - ':playgames:compileDebugSources': UP_TO_DATE, - ':playgames:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':playgames:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':playgames:createFullJarDebug': FROM_CACHE, - ':playgames:extractDebugAnnotations': FROM_CACHE, - ':playgames:extractDeepLinksDebug': FROM_CACHE, - ':playgames:generateDebugAssets': UP_TO_DATE, - ':playgames:generateDebugBuildConfig': FROM_CACHE, - ':playgames:generateDebugRFile': FROM_CACHE, - ':playgames:generateDebugResValues': FROM_CACHE, - ':playgames:generateDebugResources': UP_TO_DATE, - ':playgames:javaPreCompileDebug': FROM_CACHE, - ':playgames:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':playgames:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':playgames:mergeDebugJavaResource': SUCCESS, - ':playgames:mergeDebugJniLibFolders': FROM_CACHE, - ':playgames:mergeDebugNativeLibs': NO_SOURCE, - ':playgames:mergeDebugShaders': FROM_CACHE, - ':playgames:packageDebugAssets': FROM_CACHE, - ':playgames:packageDebugRenderscript': NO_SOURCE, - ':playgames:packageDebugResources': FROM_CACHE, - ':playgames:parseDebugLocalResources': FROM_CACHE, - ':playgames:preBuild': UP_TO_DATE, - ':playgames:preDebugBuild': UP_TO_DATE, - ':playgames:prepareDebugArtProfile': SUCCESS, - ':playgames:prepareLintJarForPublish': SUCCESS, - ':playgames:processDebugJavaRes': NO_SOURCE, - ':playgames:processDebugManifest': FROM_CACHE, - ':playgames:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:syncDebugLibJars': FROM_CACHE, - ':playgames:writeDebugAarMetadata': FROM_CACHE, - ':presenttoss:assembleDebug': SUCCESS, - ':presenttoss:checkDebugAarMetadata': FROM_CACHE, - ':presenttoss:checkDebugDuplicateClasses': FROM_CACHE, - ':presenttoss:compileDebugAidl': NO_SOURCE, - ':presenttoss:compileDebugJavaWithJavac': FROM_CACHE, - ':presenttoss:compileDebugRenderscript': NO_SOURCE, - ':presenttoss:compileDebugShaders': NO_SOURCE, - ':presenttoss:compileDebugSources': UP_TO_DATE, - ':presenttoss:compressDebugAssets': FROM_CACHE, - ':presenttoss:createDebugCompatibleScreenManifests': FROM_CACHE, - ':presenttoss:desugarDebugFileDependencies': FROM_CACHE, - ':presenttoss:dexBuilderDebug': FROM_CACHE, - ':presenttoss:extractDeepLinksDebug': FROM_CACHE, - ':presenttoss:featureDebugWriter': SUCCESS, - ':presenttoss:generateDebugAssets': UP_TO_DATE, - ':presenttoss:generateDebugBuildConfig': FROM_CACHE, - ':presenttoss:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':presenttoss:generateDebugResValues': FROM_CACHE, - ':presenttoss:generateDebugResources': UP_TO_DATE, - ':presenttoss:javaPreCompileDebug': FROM_CACHE, - ':presenttoss:mergeDebugAssets': FROM_CACHE, - ':presenttoss:mergeDebugJavaResource': SUCCESS, - ':presenttoss:mergeDebugJniLibFolders': FROM_CACHE, - ':presenttoss:mergeDebugNativeLibs': NO_SOURCE, - ':presenttoss:mergeDebugResources': SUCCESS, - ':presenttoss:mergeDebugShaders': FROM_CACHE, - ':presenttoss:mergeExtDexDebug': FROM_CACHE, - ':presenttoss:mergeLibDexDebug': FROM_CACHE, - ':presenttoss:mergeProjectDexDebug': FROM_CACHE, - ':presenttoss:packageDebug': SUCCESS, - ':presenttoss:preBuild': UP_TO_DATE, - ':presenttoss:preDebugBuild': UP_TO_DATE, - ':presenttoss:processApplicationManifestDebugForBundle': FROM_CACHE, - ':presenttoss:processDebugJavaRes': NO_SOURCE, - ':presenttoss:processDebugMainManifest': FROM_CACHE, - ':presenttoss:processDebugManifest': FROM_CACHE, - ':presenttoss:processDebugManifestForPackage': FROM_CACHE, - ':presenttoss:processDebugResources': SUCCESS, - ':presenttoss:processManifestDebugForFeature': FROM_CACHE, - ':presenttoss:stripDebugDebugSymbols': NO_SOURCE, - ':rocketsleigh:assembleDebug': SUCCESS, - ':rocketsleigh:checkDebugAarMetadata': FROM_CACHE, - ':rocketsleigh:checkDebugDuplicateClasses': FROM_CACHE, - ':rocketsleigh:compileDebugAidl': NO_SOURCE, - ':rocketsleigh:compileDebugJavaWithJavac': FROM_CACHE, - ':rocketsleigh:compileDebugKotlin': FROM_CACHE, - ':rocketsleigh:compileDebugRenderscript': NO_SOURCE, - ':rocketsleigh:compileDebugShaders': NO_SOURCE, - ':rocketsleigh:compileDebugSources': UP_TO_DATE, - ':rocketsleigh:compressDebugAssets': FROM_CACHE, - ':rocketsleigh:createDebugCompatibleScreenManifests': FROM_CACHE, - ':rocketsleigh:desugarDebugFileDependencies': FROM_CACHE, - ':rocketsleigh:dexBuilderDebug': FROM_CACHE, - ':rocketsleigh:extractDeepLinksDebug': FROM_CACHE, - ':rocketsleigh:featureDebugWriter': SUCCESS, - ':rocketsleigh:generateDebugAssets': UP_TO_DATE, - ':rocketsleigh:generateDebugBuildConfig': FROM_CACHE, - ':rocketsleigh:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':rocketsleigh:generateDebugResValues': FROM_CACHE, - ':rocketsleigh:generateDebugResources': UP_TO_DATE, - ':rocketsleigh:javaPreCompileDebug': FROM_CACHE, - ':rocketsleigh:mergeDebugAssets': FROM_CACHE, - ':rocketsleigh:mergeDebugJavaResource': SUCCESS, - ':rocketsleigh:mergeDebugJniLibFolders': FROM_CACHE, - ':rocketsleigh:mergeDebugNativeLibs': NO_SOURCE, - ':rocketsleigh:mergeDebugResources': SUCCESS, - ':rocketsleigh:mergeDebugShaders': FROM_CACHE, - ':rocketsleigh:mergeExtDexDebug': FROM_CACHE, - ':rocketsleigh:mergeLibDexDebug': FROM_CACHE, - ':rocketsleigh:mergeProjectDexDebug': FROM_CACHE, - ':rocketsleigh:packageDebug': SUCCESS, - ':rocketsleigh:preBuild': UP_TO_DATE, - ':rocketsleigh:preDebugBuild': UP_TO_DATE, - ':rocketsleigh:processApplicationManifestDebugForBundle': FROM_CACHE, - ':rocketsleigh:processDebugJavaRes': NO_SOURCE, - ':rocketsleigh:processDebugMainManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifestForPackage': FROM_CACHE, - ':rocketsleigh:processDebugResources': SUCCESS, - ':rocketsleigh:processManifestDebugForFeature': FROM_CACHE, - ':rocketsleigh:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:assembleDebug': SUCCESS, - ':santa-tracker:bundleDebugClasses': FROM_CACHE, - ':santa-tracker:checkDebugAarMetadata': FROM_CACHE, - ':santa-tracker:checkDebugDuplicateClasses': FROM_CACHE, - ':santa-tracker:checkDebugLibraries': FROM_CACHE, - ':santa-tracker:compileDebugAidl': NO_SOURCE, - ':santa-tracker:compileDebugJavaWithJavac': FROM_CACHE, - ':santa-tracker:compileDebugKotlin': FROM_CACHE, - ':santa-tracker:compileDebugRenderscript': NO_SOURCE, - ':santa-tracker:compileDebugShaders': NO_SOURCE, - ':santa-tracker:compileDebugSources': UP_TO_DATE, - ':santa-tracker:compressDebugAssets': FROM_CACHE, - ':santa-tracker:createDebugCompatibleScreenManifests': FROM_CACHE, - ':santa-tracker:desugarDebugFileDependencies': FROM_CACHE, - ':santa-tracker:dexBuilderDebug': FROM_CACHE, - ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, - ':santa-tracker:generateDebugAssets': UP_TO_DATE, - ':santa-tracker:generateDebugBuildConfig': FROM_CACHE, - ':santa-tracker:generateDebugFeatureMetadata': FROM_CACHE, - ':santa-tracker:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':santa-tracker:generateDebugResValues': FROM_CACHE, - ':santa-tracker:generateDebugResources': UP_TO_DATE, - ':santa-tracker:handleDebugMicroApk': FROM_CACHE, - ':santa-tracker:javaPreCompileDebug': FROM_CACHE, - ':santa-tracker:kaptDebugKotlin': FROM_CACHE, - ':santa-tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':santa-tracker:mergeDebugAssets': FROM_CACHE, - ':santa-tracker:mergeDebugJavaResource': SUCCESS, - ':santa-tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':santa-tracker:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':santa-tracker:mergeDebugNativeLibs': NO_SOURCE, - ':santa-tracker:mergeDebugResources': SUCCESS, - ':santa-tracker:mergeDebugShaders': FROM_CACHE, - ':santa-tracker:mergeExtDexDebug': FROM_CACHE, - ':santa-tracker:mergeLibDexDebug': FROM_CACHE, - ':santa-tracker:mergeProjectDexDebug': FROM_CACHE, - ':santa-tracker:packageDebug': SUCCESS, - ':santa-tracker:preBuild': UP_TO_DATE, - ':santa-tracker:preDebugBuild': FROM_CACHE, - ':santa-tracker:processDebugJavaRes': NO_SOURCE, - ':santa-tracker:processDebugMainManifest': FROM_CACHE, - ':santa-tracker:processDebugManifest': FROM_CACHE, - ':santa-tracker:processDebugManifestForPackage': FROM_CACHE, - ':santa-tracker:processDebugResources': SUCCESS, - ':santa-tracker:signingConfigWriterDebug': FROM_CACHE, - ':santa-tracker:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:validateSigningDebug': FROM_CACHE, - ':santa-tracker:writeDebugAppMetadata': FROM_CACHE, - ':santa-tracker:writeDebugModuleMetadata': SUCCESS, - ':santa-tracker:writeDebugSigningConfigVersions': FROM_CACHE, - ':snowballrun:assembleDebug': SUCCESS, - ':snowballrun:checkDebugAarMetadata': FROM_CACHE, - ':snowballrun:checkDebugDuplicateClasses': FROM_CACHE, - ':snowballrun:compileDebugAidl': NO_SOURCE, - ':snowballrun:compileDebugJavaWithJavac': FROM_CACHE, - ':snowballrun:compileDebugRenderscript': NO_SOURCE, - ':snowballrun:compileDebugShaders': NO_SOURCE, - ':snowballrun:compileDebugSources': UP_TO_DATE, - ':snowballrun:compressDebugAssets': FROM_CACHE, - ':snowballrun:createDebugCompatibleScreenManifests': FROM_CACHE, - ':snowballrun:desugarDebugFileDependencies': FROM_CACHE, - ':snowballrun:dexBuilderDebug': FROM_CACHE, - ':snowballrun:extractDeepLinksDebug': FROM_CACHE, - ':snowballrun:featureDebugWriter': SUCCESS, - ':snowballrun:generateDebugAssets': UP_TO_DATE, - ':snowballrun:generateDebugBuildConfig': FROM_CACHE, - ':snowballrun:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':snowballrun:generateDebugResValues': FROM_CACHE, - ':snowballrun:generateDebugResources': UP_TO_DATE, - ':snowballrun:javaPreCompileDebug': FROM_CACHE, - ':snowballrun:mergeDebugAssets': FROM_CACHE, - ':snowballrun:mergeDebugJavaResource': SUCCESS, - ':snowballrun:mergeDebugJniLibFolders': FROM_CACHE, - ':snowballrun:mergeDebugNativeLibs': NO_SOURCE, - ':snowballrun:mergeDebugResources': SUCCESS, - ':snowballrun:mergeDebugShaders': FROM_CACHE, - ':snowballrun:mergeExtDexDebug': FROM_CACHE, - ':snowballrun:mergeLibDexDebug': FROM_CACHE, - ':snowballrun:mergeProjectDexDebug': FROM_CACHE, - ':snowballrun:packageDebug': SUCCESS, - ':snowballrun:preBuild': UP_TO_DATE, - ':snowballrun:preDebugBuild': UP_TO_DATE, - ':snowballrun:processApplicationManifestDebugForBundle': FROM_CACHE, - ':snowballrun:processDebugJavaRes': NO_SOURCE, - ':snowballrun:processDebugMainManifest': FROM_CACHE, - ':snowballrun:processDebugManifest': FROM_CACHE, - ':snowballrun:processDebugManifestForPackage': FROM_CACHE, - ':snowballrun:processDebugResources': SUCCESS, - ':snowballrun:processManifestDebugForFeature': FROM_CACHE, - ':snowballrun:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:assembleDebug': SUCCESS, - ':tracker:bundleDebugAar': SUCCESS, - ':tracker:bundleLibCompileToJarDebug': FROM_CACHE, - ':tracker:bundleLibResDebug': FROM_CACHE, - ':tracker:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':tracker:compileDebugAidl': NO_SOURCE, - ':tracker:compileDebugJavaWithJavac': SUCCESS, - ':tracker:compileDebugKotlin': FROM_CACHE, - ':tracker:compileDebugLibraryResources': SUCCESS, - ':tracker:compileDebugRenderscript': NO_SOURCE, - ':tracker:compileDebugShaders': NO_SOURCE, - ':tracker:compileDebugSources': SUCCESS, - ':tracker:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':tracker:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':tracker:createFullJarDebug': FROM_CACHE, - ':tracker:extractDebugAnnotations': FROM_CACHE, - ':tracker:extractDeepLinksDebug': FROM_CACHE, - ':tracker:generateDebugAssets': UP_TO_DATE, - ':tracker:generateDebugBuildConfig': FROM_CACHE, - ':tracker:generateDebugRFile': FROM_CACHE, - ':tracker:generateDebugResValues': FROM_CACHE, - ':tracker:generateDebugResources': UP_TO_DATE, - ':tracker:javaPreCompileDebug': FROM_CACHE, - ':tracker:kaptDebugKotlin': FROM_CACHE, - ':tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':tracker:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':tracker:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':tracker:mergeDebugJavaResource': SUCCESS, - ':tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':tracker:mergeDebugNativeLibs': NO_SOURCE, - ':tracker:mergeDebugResources': FROM_CACHE, - ':tracker:mergeDebugShaders': FROM_CACHE, - ':tracker:packageDebugAssets': FROM_CACHE, - ':tracker:packageDebugRenderscript': NO_SOURCE, - ':tracker:packageDebugResources': FROM_CACHE, - ':tracker:parseDebugLocalResources': FROM_CACHE, - ':tracker:preBuild': UP_TO_DATE, - ':tracker:preDebugBuild': UP_TO_DATE, - ':tracker:prepareDebugArtProfile': SUCCESS, - ':tracker:prepareLintJarForPublish': SUCCESS, - ':tracker:processDebugJavaRes': NO_SOURCE, - ':tracker:processDebugManifest': FROM_CACHE, - ':tracker:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:syncDebugLibJars': FROM_CACHE, - ':tracker:writeDebugAarMetadata': FROM_CACHE, - ':wearable:assembleDebug': SUCCESS, - ':wearable:checkDebugAarMetadata': FROM_CACHE, - ':wearable:checkDebugDuplicateClasses': FROM_CACHE, - ':wearable:compileDebugAidl': NO_SOURCE, - ':wearable:compileDebugJavaWithJavac': FROM_CACHE, - ':wearable:compileDebugKotlin': FROM_CACHE, - ':wearable:compileDebugRenderscript': NO_SOURCE, - ':wearable:compileDebugShaders': NO_SOURCE, - ':wearable:compileDebugSources': UP_TO_DATE, - ':wearable:compressDebugAssets': FROM_CACHE, - ':wearable:createDebugCompatibleScreenManifests': FROM_CACHE, - ':wearable:desugarDebugFileDependencies': FROM_CACHE, - ':wearable:dexBuilderDebug': FROM_CACHE, - ':wearable:extractDeepLinksDebug': FROM_CACHE, - ':wearable:generateDebugAssets': UP_TO_DATE, - ':wearable:generateDebugBuildConfig': FROM_CACHE, - ':wearable:generateDebugResValues': FROM_CACHE, - ':wearable:generateDebugResources': UP_TO_DATE, - ':wearable:javaPreCompileDebug': FROM_CACHE, - ':wearable:kaptDebugKotlin': SKIPPED, - ':wearable:kaptGenerateStubsDebugKotlin': SKIPPED, - ':wearable:mergeDebugAssets': FROM_CACHE, - ':wearable:mergeDebugJavaResource': SUCCESS, - ':wearable:mergeDebugJniLibFolders': FROM_CACHE, - ':wearable:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':wearable:mergeDebugNativeLibs': NO_SOURCE, - ':wearable:mergeDebugResources': SUCCESS, - ':wearable:mergeDebugShaders': FROM_CACHE, - ':wearable:mergeExtDexDebug': FROM_CACHE, - ':wearable:mergeLibDexDebug': FROM_CACHE, - ':wearable:mergeProjectDexDebug': FROM_CACHE, - ':wearable:packageDebug': SUCCESS, - ':wearable:preBuild': UP_TO_DATE, - ':wearable:preDebugBuild': UP_TO_DATE, - ':wearable:processDebugJavaRes': NO_SOURCE, - ':wearable:processDebugMainManifest': FROM_CACHE, - ':wearable:processDebugManifest': FROM_CACHE, - ':wearable:processDebugManifestForPackage': FROM_CACHE, - ':wearable:processDebugResources': SUCCESS, - ':wearable:stripDebugDebugSymbols': NO_SOURCE, - ':wearable:validateSigningDebug': FROM_CACHE, - ':wearable:writeDebugAppMetadata': FROM_CACHE, - ':wearable:writeDebugSigningConfigVersions': FROM_CACHE, - ] -} - -class AndroidPluginExpectations4 { - - static final EXPECTED_RESULTS_4_2 = [ - ':cityquiz:assembleDebug': SUCCESS, - ':cityquiz:checkDebugAarMetadata': FROM_CACHE, - ':cityquiz:checkDebugDuplicateClasses': FROM_CACHE, - ':cityquiz:compileDebugAidl': NO_SOURCE, - ':cityquiz:compileDebugJavaWithJavac': FROM_CACHE, - ':cityquiz:compileDebugKotlin': FROM_CACHE, - ':cityquiz:compileDebugRenderscript': NO_SOURCE, - ':cityquiz:compileDebugShaders': NO_SOURCE, - ':cityquiz:compileDebugSources': UP_TO_DATE, - ':cityquiz:compressDebugAssets': FROM_CACHE, - ':cityquiz:createDebugCompatibleScreenManifests': FROM_CACHE, - ':cityquiz:desugarDebugFileDependencies': SUCCESS, - ':cityquiz:dexBuilderDebug': FROM_CACHE, - ':cityquiz:extractDeepLinksDebug': FROM_CACHE, - ':cityquiz:featureDebugWriter': SUCCESS, - ':cityquiz:generateDebugAssets': UP_TO_DATE, - ':cityquiz:generateDebugBuildConfig': FROM_CACHE, - ':cityquiz:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':cityquiz:generateDebugResValues': FROM_CACHE, - ':cityquiz:generateDebugResources': UP_TO_DATE, - ':cityquiz:javaPreCompileDebug': FROM_CACHE, - ':cityquiz:mergeDebugAssets': FROM_CACHE, - ':cityquiz:mergeDebugJavaResource': FROM_CACHE, - ':cityquiz:mergeDebugJniLibFolders': FROM_CACHE, - ':cityquiz:mergeDebugNativeLibs': NO_SOURCE, - ':cityquiz:mergeDebugResources': SUCCESS, - ':cityquiz:mergeDebugShaders': FROM_CACHE, - ':cityquiz:mergeExtDexDebug': FROM_CACHE, - ':cityquiz:mergeLibDexDebug': FROM_CACHE, - ':cityquiz:mergeProjectDexDebug': FROM_CACHE, - ':cityquiz:packageDebug': SUCCESS, - ':cityquiz:preBuild': UP_TO_DATE, - ':cityquiz:preDebugBuild': UP_TO_DATE, - ':cityquiz:processApplicationManifestDebugForBundle': FROM_CACHE, - ':cityquiz:processDebugJavaRes': NO_SOURCE, - ':cityquiz:processDebugMainManifest': FROM_CACHE, - ':cityquiz:processDebugManifest': FROM_CACHE, - ':cityquiz:processDebugManifestForPackage': FROM_CACHE, - ':cityquiz:processDebugResources': SUCCESS, - ':cityquiz:processManifestDebugForFeature': FROM_CACHE, - ':cityquiz:stripDebugDebugSymbols': NO_SOURCE, - ':common:assembleDebug': SUCCESS, - ':common:bundleDebugAar': SUCCESS, - ':common:bundleLibCompileToJarDebug': FROM_CACHE, - ':common:bundleLibResDebug': FROM_CACHE, - ':common:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':common:compileDebugAidl': NO_SOURCE, - ':common:compileDebugJavaWithJavac': FROM_CACHE, - ':common:compileDebugKotlin': FROM_CACHE, - ':common:compileDebugLibraryResources': SUCCESS, - ':common:compileDebugRenderscript': NO_SOURCE, - ':common:compileDebugShaders': NO_SOURCE, - ':common:compileDebugSources': UP_TO_DATE, - ':common:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':common:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':common:createFullJarDebug': FROM_CACHE, - ':common:extractDebugAnnotations': FROM_CACHE, - ':common:extractDeepLinksDebug': FROM_CACHE, - ':common:generateDebugAssets': UP_TO_DATE, - ':common:generateDebugBuildConfig': FROM_CACHE, - ':common:generateDebugRFile': FROM_CACHE, - ':common:generateDebugResValues': FROM_CACHE, - ':common:generateDebugResources': UP_TO_DATE, - ':common:javaPreCompileDebug': FROM_CACHE, - ':common:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':common:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':common:mergeDebugJavaResource': FROM_CACHE, - ':common:mergeDebugJniLibFolders': FROM_CACHE, - ':common:mergeDebugNativeLibs': NO_SOURCE, - ':common:mergeDebugShaders': FROM_CACHE, - ':common:packageDebugAssets': FROM_CACHE, - ':common:packageDebugRenderscript': NO_SOURCE, - ':common:packageDebugResources': FROM_CACHE, - ':common:parseDebugLocalResources': FROM_CACHE, - ':common:preBuild': UP_TO_DATE, - ':common:preDebugBuild': UP_TO_DATE, - ':common:prepareLintJarForPublish': SUCCESS, - ':common:processDebugJavaRes': NO_SOURCE, - ':common:processDebugManifest': FROM_CACHE, - ':common:stripDebugDebugSymbols': NO_SOURCE, - ':common:syncDebugLibJars': FROM_CACHE, - ':common:writeDebugAarMetadata': FROM_CACHE, - ':dasherdancer:assembleDebug': SUCCESS, - ':dasherdancer:checkDebugAarMetadata': FROM_CACHE, - ':dasherdancer:checkDebugDuplicateClasses': FROM_CACHE, - ':dasherdancer:compileDebugAidl': NO_SOURCE, - ':dasherdancer:compileDebugJavaWithJavac': FROM_CACHE, - ':dasherdancer:compileDebugKotlin': FROM_CACHE, - ':dasherdancer:compileDebugRenderscript': NO_SOURCE, - ':dasherdancer:compileDebugShaders': NO_SOURCE, - ':dasherdancer:compileDebugSources': UP_TO_DATE, - ':dasherdancer:compressDebugAssets': FROM_CACHE, - ':dasherdancer:createDebugCompatibleScreenManifests': FROM_CACHE, - ':dasherdancer:desugarDebugFileDependencies': SUCCESS, - ':dasherdancer:dexBuilderDebug': FROM_CACHE, - ':dasherdancer:extractDeepLinksDebug': FROM_CACHE, - ':dasherdancer:featureDebugWriter': SUCCESS, - ':dasherdancer:generateDebugAssets': UP_TO_DATE, - ':dasherdancer:generateDebugBuildConfig': FROM_CACHE, - ':dasherdancer:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':dasherdancer:generateDebugResValues': FROM_CACHE, - ':dasherdancer:generateDebugResources': UP_TO_DATE, - ':dasherdancer:javaPreCompileDebug': FROM_CACHE, - ':dasherdancer:mergeDebugAssets': FROM_CACHE, - ':dasherdancer:mergeDebugJavaResource': FROM_CACHE, - ':dasherdancer:mergeDebugJniLibFolders': FROM_CACHE, - ':dasherdancer:mergeDebugNativeLibs': NO_SOURCE, - ':dasherdancer:mergeDebugResources': SUCCESS, - ':dasherdancer:mergeDebugShaders': FROM_CACHE, - ':dasherdancer:mergeExtDexDebug': FROM_CACHE, - ':dasherdancer:mergeLibDexDebug': FROM_CACHE, - ':dasherdancer:mergeProjectDexDebug': FROM_CACHE, - ':dasherdancer:packageDebug': SUCCESS, - ':dasherdancer:preBuild': UP_TO_DATE, - ':dasherdancer:preDebugBuild': UP_TO_DATE, - ':dasherdancer:processApplicationManifestDebugForBundle': FROM_CACHE, - ':dasherdancer:processDebugJavaRes': NO_SOURCE, - ':dasherdancer:processDebugMainManifest': FROM_CACHE, - ':dasherdancer:processDebugManifest': FROM_CACHE, - ':dasherdancer:processDebugManifestForPackage': FROM_CACHE, - ':dasherdancer:processDebugResources': SUCCESS, - ':dasherdancer:processManifestDebugForFeature': FROM_CACHE, - ':dasherdancer:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:assembleDebug': SUCCESS, - ':doodles-lib:bundleDebugAar': SUCCESS, - ':doodles-lib:bundleLibCompileToJarDebug': FROM_CACHE, - ':doodles-lib:bundleLibResDebug': NO_SOURCE, - ':doodles-lib:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':doodles-lib:compileDebugAidl': NO_SOURCE, - ':doodles-lib:compileDebugJavaWithJavac': FROM_CACHE, - ':doodles-lib:compileDebugLibraryResources': SUCCESS, - ':doodles-lib:compileDebugRenderscript': NO_SOURCE, - ':doodles-lib:compileDebugShaders': NO_SOURCE, - ':doodles-lib:compileDebugSources': UP_TO_DATE, - ':doodles-lib:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':doodles-lib:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':doodles-lib:createFullJarDebug': FROM_CACHE, - ':doodles-lib:extractDebugAnnotations': FROM_CACHE, - ':doodles-lib:extractDeepLinksDebug': FROM_CACHE, - ':doodles-lib:generateDebugAssets': UP_TO_DATE, - ':doodles-lib:generateDebugBuildConfig': FROM_CACHE, - ':doodles-lib:generateDebugRFile': FROM_CACHE, - ':doodles-lib:generateDebugResValues': FROM_CACHE, - ':doodles-lib:generateDebugResources': UP_TO_DATE, - ':doodles-lib:javaPreCompileDebug': FROM_CACHE, - ':doodles-lib:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugJavaResource': FROM_CACHE, - ':doodles-lib:mergeDebugJniLibFolders': FROM_CACHE, - ':doodles-lib:mergeDebugNativeLibs': NO_SOURCE, - ':doodles-lib:mergeDebugShaders': FROM_CACHE, - ':doodles-lib:packageDebugAssets': FROM_CACHE, - ':doodles-lib:packageDebugRenderscript': NO_SOURCE, - ':doodles-lib:packageDebugResources': FROM_CACHE, - ':doodles-lib:parseDebugLocalResources': FROM_CACHE, - ':doodles-lib:preBuild': UP_TO_DATE, - ':doodles-lib:preDebugBuild': UP_TO_DATE, - ':doodles-lib:prepareLintJarForPublish': SUCCESS, - ':doodles-lib:processDebugJavaRes': NO_SOURCE, - ':doodles-lib:processDebugManifest': FROM_CACHE, - ':doodles-lib:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:syncDebugLibJars': FROM_CACHE, - ':doodles-lib:writeDebugAarMetadata': FROM_CACHE, - ':gumball:assembleDebug': SUCCESS, - ':gumball:checkDebugAarMetadata': FROM_CACHE, - ':gumball:checkDebugDuplicateClasses': FROM_CACHE, - ':gumball:compileDebugAidl': NO_SOURCE, - ':gumball:compileDebugJavaWithJavac': FROM_CACHE, - ':gumball:compileDebugRenderscript': NO_SOURCE, - ':gumball:compileDebugShaders': NO_SOURCE, - ':gumball:compileDebugSources': UP_TO_DATE, - ':gumball:compressDebugAssets': FROM_CACHE, - ':gumball:createDebugCompatibleScreenManifests': FROM_CACHE, - ':gumball:desugarDebugFileDependencies': SUCCESS, - ':gumball:dexBuilderDebug': FROM_CACHE, - ':gumball:extractDeepLinksDebug': FROM_CACHE, - ':gumball:featureDebugWriter': SUCCESS, - ':gumball:generateDebugAssets': UP_TO_DATE, - ':gumball:generateDebugBuildConfig': FROM_CACHE, - ':gumball:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':gumball:generateDebugResValues': FROM_CACHE, - ':gumball:generateDebugResources': UP_TO_DATE, - ':gumball:javaPreCompileDebug': FROM_CACHE, - ':gumball:mergeDebugAssets': FROM_CACHE, - ':gumball:mergeDebugJavaResource': FROM_CACHE, - ':gumball:mergeDebugJniLibFolders': FROM_CACHE, - ':gumball:mergeDebugNativeLibs': FROM_CACHE, - ':gumball:mergeDebugResources': SUCCESS, - ':gumball:mergeDebugShaders': FROM_CACHE, - ':gumball:mergeExtDexDebug': FROM_CACHE, - ':gumball:mergeLibDexDebug': FROM_CACHE, - ':gumball:mergeProjectDexDebug': FROM_CACHE, - ':gumball:packageDebug': SUCCESS, - ':gumball:preBuild': UP_TO_DATE, - ':gumball:preDebugBuild': UP_TO_DATE, - ':gumball:processApplicationManifestDebugForBundle': FROM_CACHE, - ':gumball:processDebugJavaRes': NO_SOURCE, - ':gumball:processDebugMainManifest': FROM_CACHE, - ':gumball:processDebugManifest': FROM_CACHE, - ':gumball:processDebugManifestForPackage': FROM_CACHE, - ':gumball:processDebugResources': SUCCESS, - ':gumball:processManifestDebugForFeature': FROM_CACHE, - ':gumball:stripDebugDebugSymbols': NO_SOURCE, - ':jetpack:assembleDebug': SUCCESS, - ':jetpack:checkDebugAarMetadata': FROM_CACHE, - ':jetpack:checkDebugDuplicateClasses': FROM_CACHE, - ':jetpack:compileDebugAidl': NO_SOURCE, - ':jetpack:compileDebugJavaWithJavac': FROM_CACHE, - ':jetpack:compileDebugKotlin': FROM_CACHE, - ':jetpack:compileDebugRenderscript': NO_SOURCE, - ':jetpack:compileDebugShaders': NO_SOURCE, - ':jetpack:compileDebugSources': UP_TO_DATE, - ':jetpack:compressDebugAssets': FROM_CACHE, - ':jetpack:createDebugCompatibleScreenManifests': FROM_CACHE, - ':jetpack:desugarDebugFileDependencies': SUCCESS, - ':jetpack:dexBuilderDebug': FROM_CACHE, - ':jetpack:extractDeepLinksDebug': FROM_CACHE, - ':jetpack:featureDebugWriter': SUCCESS, - ':jetpack:generateDebugAssets': UP_TO_DATE, - ':jetpack:generateDebugBuildConfig': FROM_CACHE, - ':jetpack:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':jetpack:generateDebugResValues': FROM_CACHE, - ':jetpack:generateDebugResources': UP_TO_DATE, - ':jetpack:javaPreCompileDebug': FROM_CACHE, - ':jetpack:mergeDebugAssets': FROM_CACHE, - ':jetpack:mergeDebugJavaResource': FROM_CACHE, - ':jetpack:mergeDebugJniLibFolders': FROM_CACHE, - ':jetpack:mergeDebugNativeLibs': NO_SOURCE, - ':jetpack:mergeDebugResources': SUCCESS, - ':jetpack:mergeDebugShaders': FROM_CACHE, - ':jetpack:mergeExtDexDebug': FROM_CACHE, - ':jetpack:mergeLibDexDebug': FROM_CACHE, - ':jetpack:mergeProjectDexDebug': FROM_CACHE, - ':jetpack:packageDebug': SUCCESS, - ':jetpack:preBuild': UP_TO_DATE, - ':jetpack:preDebugBuild': UP_TO_DATE, - ':jetpack:processApplicationManifestDebugForBundle': FROM_CACHE, - ':jetpack:processDebugJavaRes': NO_SOURCE, - ':jetpack:processDebugMainManifest': FROM_CACHE, - ':jetpack:processDebugManifest': FROM_CACHE, - ':jetpack:processDebugManifestForPackage': FROM_CACHE, - ':jetpack:processDebugResources': SUCCESS, - ':jetpack:processManifestDebugForFeature': FROM_CACHE, - ':jetpack:stripDebugDebugSymbols': NO_SOURCE, - ':memory:assembleDebug': SUCCESS, - ':memory:checkDebugAarMetadata': FROM_CACHE, - ':memory:checkDebugDuplicateClasses': FROM_CACHE, - ':memory:compileDebugAidl': NO_SOURCE, - ':memory:compileDebugJavaWithJavac': FROM_CACHE, - ':memory:compileDebugRenderscript': NO_SOURCE, - ':memory:compileDebugShaders': NO_SOURCE, - ':memory:compileDebugSources': UP_TO_DATE, - ':memory:compressDebugAssets': FROM_CACHE, - ':memory:createDebugCompatibleScreenManifests': FROM_CACHE, - ':memory:desugarDebugFileDependencies': SUCCESS, - ':memory:dexBuilderDebug': FROM_CACHE, - ':memory:extractDeepLinksDebug': FROM_CACHE, - ':memory:featureDebugWriter': SUCCESS, - ':memory:generateDebugAssets': UP_TO_DATE, - ':memory:generateDebugBuildConfig': FROM_CACHE, - ':memory:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':memory:generateDebugResValues': FROM_CACHE, - ':memory:generateDebugResources': UP_TO_DATE, - ':memory:javaPreCompileDebug': FROM_CACHE, - ':memory:mergeDebugAssets': FROM_CACHE, - ':memory:mergeDebugJavaResource': FROM_CACHE, - ':memory:mergeDebugJniLibFolders': FROM_CACHE, - ':memory:mergeDebugNativeLibs': NO_SOURCE, - ':memory:mergeDebugResources': SUCCESS, - ':memory:mergeDebugShaders': FROM_CACHE, - ':memory:mergeExtDexDebug': FROM_CACHE, - ':memory:mergeLibDexDebug': FROM_CACHE, - ':memory:mergeProjectDexDebug': FROM_CACHE, - ':memory:packageDebug': SUCCESS, - ':memory:preBuild': UP_TO_DATE, - ':memory:preDebugBuild': UP_TO_DATE, - ':memory:processApplicationManifestDebugForBundle': FROM_CACHE, - ':memory:processDebugJavaRes': NO_SOURCE, - ':memory:processDebugMainManifest': FROM_CACHE, - ':memory:processDebugManifest': FROM_CACHE, - ':memory:processDebugManifestForPackage': FROM_CACHE, - ':memory:processDebugResources': SUCCESS, - ':memory:processManifestDebugForFeature': FROM_CACHE, - ':memory:stripDebugDebugSymbols': NO_SOURCE, - ':penguinswim:assembleDebug': SUCCESS, - ':penguinswim:checkDebugAarMetadata': FROM_CACHE, - ':penguinswim:checkDebugDuplicateClasses': FROM_CACHE, - ':penguinswim:compileDebugAidl': NO_SOURCE, - ':penguinswim:compileDebugJavaWithJavac': FROM_CACHE, - ':penguinswim:compileDebugRenderscript': NO_SOURCE, - ':penguinswim:compileDebugShaders': NO_SOURCE, - ':penguinswim:compileDebugSources': UP_TO_DATE, - ':penguinswim:compressDebugAssets': FROM_CACHE, - ':penguinswim:createDebugCompatibleScreenManifests': FROM_CACHE, - ':penguinswim:desugarDebugFileDependencies': SUCCESS, - ':penguinswim:dexBuilderDebug': FROM_CACHE, - ':penguinswim:extractDeepLinksDebug': FROM_CACHE, - ':penguinswim:featureDebugWriter': SUCCESS, - ':penguinswim:generateDebugAssets': UP_TO_DATE, - ':penguinswim:generateDebugBuildConfig': FROM_CACHE, - ':penguinswim:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':penguinswim:generateDebugResValues': FROM_CACHE, - ':penguinswim:generateDebugResources': UP_TO_DATE, - ':penguinswim:javaPreCompileDebug': FROM_CACHE, - ':penguinswim:mergeDebugAssets': FROM_CACHE, - ':penguinswim:mergeDebugJavaResource': FROM_CACHE, - ':penguinswim:mergeDebugJniLibFolders': FROM_CACHE, - ':penguinswim:mergeDebugNativeLibs': NO_SOURCE, - ':penguinswim:mergeDebugResources': SUCCESS, - ':penguinswim:mergeDebugShaders': FROM_CACHE, - ':penguinswim:mergeExtDexDebug': FROM_CACHE, - ':penguinswim:mergeLibDexDebug': FROM_CACHE, - ':penguinswim:mergeProjectDexDebug': FROM_CACHE, - ':penguinswim:packageDebug': SUCCESS, - ':penguinswim:preBuild': UP_TO_DATE, - ':penguinswim:preDebugBuild': UP_TO_DATE, - ':penguinswim:processApplicationManifestDebugForBundle': FROM_CACHE, - ':penguinswim:processDebugJavaRes': NO_SOURCE, - ':penguinswim:processDebugMainManifest': FROM_CACHE, - ':penguinswim:processDebugManifest': FROM_CACHE, - ':penguinswim:processDebugManifestForPackage': FROM_CACHE, - ':penguinswim:processDebugResources': SUCCESS, - ':penguinswim:processManifestDebugForFeature': FROM_CACHE, - ':penguinswim:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:assembleDebug': SUCCESS, - ':playgames:bundleDebugAar': SUCCESS, - ':playgames:bundleLibCompileToJarDebug': FROM_CACHE, - ':playgames:bundleLibResDebug': NO_SOURCE, - ':playgames:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':playgames:compileDebugAidl': NO_SOURCE, - ':playgames:compileDebugJavaWithJavac': FROM_CACHE, - ':playgames:compileDebugLibraryResources': SUCCESS, - ':playgames:compileDebugRenderscript': NO_SOURCE, - ':playgames:compileDebugShaders': NO_SOURCE, - ':playgames:compileDebugSources': UP_TO_DATE, - ':playgames:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':playgames:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':playgames:createFullJarDebug': FROM_CACHE, - ':playgames:extractDebugAnnotations': FROM_CACHE, - ':playgames:extractDeepLinksDebug': FROM_CACHE, - ':playgames:generateDebugAssets': UP_TO_DATE, - ':playgames:generateDebugBuildConfig': FROM_CACHE, - ':playgames:generateDebugRFile': FROM_CACHE, - ':playgames:generateDebugResValues': FROM_CACHE, - ':playgames:generateDebugResources': UP_TO_DATE, - ':playgames:javaPreCompileDebug': FROM_CACHE, - ':playgames:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':playgames:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':playgames:mergeDebugJavaResource': FROM_CACHE, - ':playgames:mergeDebugJniLibFolders': FROM_CACHE, - ':playgames:mergeDebugNativeLibs': NO_SOURCE, - ':playgames:mergeDebugShaders': FROM_CACHE, - ':playgames:packageDebugAssets': FROM_CACHE, - ':playgames:packageDebugRenderscript': NO_SOURCE, - ':playgames:packageDebugResources': FROM_CACHE, - ':playgames:parseDebugLocalResources': FROM_CACHE, - ':playgames:preBuild': UP_TO_DATE, - ':playgames:preDebugBuild': UP_TO_DATE, - ':playgames:prepareLintJarForPublish': SUCCESS, - ':playgames:processDebugJavaRes': NO_SOURCE, - ':playgames:processDebugManifest': FROM_CACHE, - ':playgames:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:syncDebugLibJars': FROM_CACHE, - ':playgames:writeDebugAarMetadata': FROM_CACHE, - ':presenttoss:assembleDebug': SUCCESS, - ':presenttoss:checkDebugAarMetadata': FROM_CACHE, - ':presenttoss:checkDebugDuplicateClasses': FROM_CACHE, - ':presenttoss:compileDebugAidl': NO_SOURCE, - ':presenttoss:compileDebugJavaWithJavac': FROM_CACHE, - ':presenttoss:compileDebugRenderscript': NO_SOURCE, - ':presenttoss:compileDebugShaders': NO_SOURCE, - ':presenttoss:compileDebugSources': UP_TO_DATE, - ':presenttoss:compressDebugAssets': FROM_CACHE, - ':presenttoss:createDebugCompatibleScreenManifests': FROM_CACHE, - ':presenttoss:desugarDebugFileDependencies': SUCCESS, - ':presenttoss:dexBuilderDebug': FROM_CACHE, - ':presenttoss:extractDeepLinksDebug': FROM_CACHE, - ':presenttoss:featureDebugWriter': SUCCESS, - ':presenttoss:generateDebugAssets': UP_TO_DATE, - ':presenttoss:generateDebugBuildConfig': FROM_CACHE, - ':presenttoss:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':presenttoss:generateDebugResValues': FROM_CACHE, - ':presenttoss:generateDebugResources': UP_TO_DATE, - ':presenttoss:javaPreCompileDebug': FROM_CACHE, - ':presenttoss:mergeDebugAssets': FROM_CACHE, - ':presenttoss:mergeDebugJavaResource': FROM_CACHE, - ':presenttoss:mergeDebugJniLibFolders': FROM_CACHE, - ':presenttoss:mergeDebugNativeLibs': NO_SOURCE, - ':presenttoss:mergeDebugResources': SUCCESS, - ':presenttoss:mergeDebugShaders': FROM_CACHE, - ':presenttoss:mergeExtDexDebug': FROM_CACHE, - ':presenttoss:mergeLibDexDebug': FROM_CACHE, - ':presenttoss:mergeProjectDexDebug': FROM_CACHE, - ':presenttoss:packageDebug': SUCCESS, - ':presenttoss:preBuild': UP_TO_DATE, - ':presenttoss:preDebugBuild': UP_TO_DATE, - ':presenttoss:processApplicationManifestDebugForBundle': FROM_CACHE, - ':presenttoss:processDebugJavaRes': NO_SOURCE, - ':presenttoss:processDebugMainManifest': FROM_CACHE, - ':presenttoss:processDebugManifest': FROM_CACHE, - ':presenttoss:processDebugManifestForPackage': FROM_CACHE, - ':presenttoss:processDebugResources': SUCCESS, - ':presenttoss:processManifestDebugForFeature': FROM_CACHE, - ':presenttoss:stripDebugDebugSymbols': NO_SOURCE, - ':rocketsleigh:assembleDebug': SUCCESS, - ':rocketsleigh:checkDebugAarMetadata': FROM_CACHE, - ':rocketsleigh:checkDebugDuplicateClasses': FROM_CACHE, - ':rocketsleigh:compileDebugAidl': NO_SOURCE, - ':rocketsleigh:compileDebugJavaWithJavac': FROM_CACHE, - ':rocketsleigh:compileDebugKotlin': FROM_CACHE, - ':rocketsleigh:compileDebugRenderscript': NO_SOURCE, - ':rocketsleigh:compileDebugShaders': NO_SOURCE, - ':rocketsleigh:compileDebugSources': UP_TO_DATE, - ':rocketsleigh:compressDebugAssets': FROM_CACHE, - ':rocketsleigh:createDebugCompatibleScreenManifests': FROM_CACHE, - ':rocketsleigh:desugarDebugFileDependencies': SUCCESS, - ':rocketsleigh:dexBuilderDebug': FROM_CACHE, - ':rocketsleigh:extractDeepLinksDebug': FROM_CACHE, - ':rocketsleigh:featureDebugWriter': SUCCESS, - ':rocketsleigh:generateDebugAssets': UP_TO_DATE, - ':rocketsleigh:generateDebugBuildConfig': FROM_CACHE, - ':rocketsleigh:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':rocketsleigh:generateDebugResValues': FROM_CACHE, - ':rocketsleigh:generateDebugResources': UP_TO_DATE, - ':rocketsleigh:javaPreCompileDebug': FROM_CACHE, - ':rocketsleigh:mergeDebugAssets': FROM_CACHE, - ':rocketsleigh:mergeDebugJavaResource': FROM_CACHE, - ':rocketsleigh:mergeDebugJniLibFolders': FROM_CACHE, - ':rocketsleigh:mergeDebugNativeLibs': NO_SOURCE, - ':rocketsleigh:mergeDebugResources': SUCCESS, - ':rocketsleigh:mergeDebugShaders': FROM_CACHE, - ':rocketsleigh:mergeExtDexDebug': FROM_CACHE, - ':rocketsleigh:mergeLibDexDebug': FROM_CACHE, - ':rocketsleigh:mergeProjectDexDebug': FROM_CACHE, - ':rocketsleigh:packageDebug': SUCCESS, - ':rocketsleigh:preBuild': UP_TO_DATE, - ':rocketsleigh:preDebugBuild': UP_TO_DATE, - ':rocketsleigh:processApplicationManifestDebugForBundle': FROM_CACHE, - ':rocketsleigh:processDebugJavaRes': NO_SOURCE, - ':rocketsleigh:processDebugMainManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifestForPackage': FROM_CACHE, - ':rocketsleigh:processDebugResources': SUCCESS, - ':rocketsleigh:processManifestDebugForFeature': FROM_CACHE, - ':rocketsleigh:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:assembleDebug': SUCCESS, - ':santa-tracker:bundleDebugClasses': FROM_CACHE, - ':santa-tracker:checkDebugAarMetadata': FROM_CACHE, - ':santa-tracker:checkDebugDuplicateClasses': FROM_CACHE, - ':santa-tracker:checkDebugLibraries': FROM_CACHE, - ':santa-tracker:compileDebugAidl': NO_SOURCE, - ':santa-tracker:compileDebugJavaWithJavac': FROM_CACHE, - ':santa-tracker:compileDebugKotlin': FROM_CACHE, - ':santa-tracker:compileDebugRenderscript': NO_SOURCE, - ':santa-tracker:compileDebugShaders': NO_SOURCE, - ':santa-tracker:compileDebugSources': UP_TO_DATE, - ':santa-tracker:compressDebugAssets': FROM_CACHE, - ':santa-tracker:createDebugCompatibleScreenManifests': FROM_CACHE, - ':santa-tracker:desugarDebugFileDependencies': SUCCESS, - ':santa-tracker:dexBuilderDebug': FROM_CACHE, - ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, - ':santa-tracker:generateDebugAssets': UP_TO_DATE, - ':santa-tracker:generateDebugBuildConfig': FROM_CACHE, - ':santa-tracker:generateDebugFeatureMetadata': FROM_CACHE, - ':santa-tracker:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':santa-tracker:generateDebugResValues': FROM_CACHE, - ':santa-tracker:generateDebugResources': UP_TO_DATE, - ':santa-tracker:handleDebugMicroApk': FROM_CACHE, - ':santa-tracker:kaptDebugKotlin': FROM_CACHE, - ':santa-tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':santa-tracker:mergeDebugAssets': FROM_CACHE, - ':santa-tracker:mergeDebugJavaResource': FROM_CACHE, - ':santa-tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':santa-tracker:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':santa-tracker:mergeDebugNativeLibs': FROM_CACHE, - ':santa-tracker:mergeDebugResources': SUCCESS, - ':santa-tracker:mergeDebugShaders': FROM_CACHE, - ':santa-tracker:mergeExtDexDebug': FROM_CACHE, - ':santa-tracker:mergeLibDexDebug': FROM_CACHE, - ':santa-tracker:mergeProjectDexDebug': FROM_CACHE, - ':santa-tracker:packageDebug': SUCCESS, - ':santa-tracker:preBuild': UP_TO_DATE, - ':santa-tracker:preDebugBuild': FROM_CACHE, - ':santa-tracker:processDebugJavaRes': NO_SOURCE, - ':santa-tracker:processDebugMainManifest': FROM_CACHE, - ':santa-tracker:processDebugManifest': FROM_CACHE, - ':santa-tracker:processDebugManifestForPackage': FROM_CACHE, - ':santa-tracker:processDebugResources': SUCCESS, - ':santa-tracker:signingConfigWriterDebug': FROM_CACHE, - ':santa-tracker:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:validateSigningDebug': FROM_CACHE, - ':santa-tracker:writeDebugAppMetadata': FROM_CACHE, - ':santa-tracker:writeDebugModuleMetadata': SUCCESS, - ':santa-tracker:writeDebugSigningConfigVersions': FROM_CACHE, - ':snowballrun:assembleDebug': SUCCESS, - ':snowballrun:checkDebugAarMetadata': FROM_CACHE, - ':snowballrun:checkDebugDuplicateClasses': FROM_CACHE, - ':snowballrun:compileDebugAidl': NO_SOURCE, - ':snowballrun:compileDebugJavaWithJavac': FROM_CACHE, - ':snowballrun:compileDebugRenderscript': NO_SOURCE, - ':snowballrun:compileDebugShaders': NO_SOURCE, - ':snowballrun:compileDebugSources': UP_TO_DATE, - ':snowballrun:compressDebugAssets': FROM_CACHE, - ':snowballrun:createDebugCompatibleScreenManifests': FROM_CACHE, - ':snowballrun:desugarDebugFileDependencies': SUCCESS, - ':snowballrun:dexBuilderDebug': FROM_CACHE, - ':snowballrun:extractDeepLinksDebug': FROM_CACHE, - ':snowballrun:featureDebugWriter': SUCCESS, - ':snowballrun:generateDebugAssets': UP_TO_DATE, - ':snowballrun:generateDebugBuildConfig': FROM_CACHE, - ':snowballrun:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':snowballrun:generateDebugResValues': FROM_CACHE, - ':snowballrun:generateDebugResources': UP_TO_DATE, - ':snowballrun:javaPreCompileDebug': FROM_CACHE, - ':snowballrun:mergeDebugAssets': FROM_CACHE, - ':snowballrun:mergeDebugJavaResource': FROM_CACHE, - ':snowballrun:mergeDebugJniLibFolders': FROM_CACHE, - ':snowballrun:mergeDebugNativeLibs': NO_SOURCE, - ':snowballrun:mergeDebugResources': SUCCESS, - ':snowballrun:mergeDebugShaders': FROM_CACHE, - ':snowballrun:mergeExtDexDebug': FROM_CACHE, - ':snowballrun:mergeLibDexDebug': FROM_CACHE, - ':snowballrun:mergeProjectDexDebug': FROM_CACHE, - ':snowballrun:packageDebug': SUCCESS, - ':snowballrun:preBuild': UP_TO_DATE, - ':snowballrun:preDebugBuild': UP_TO_DATE, - ':snowballrun:processApplicationManifestDebugForBundle': FROM_CACHE, - ':snowballrun:processDebugJavaRes': NO_SOURCE, - ':snowballrun:processDebugMainManifest': FROM_CACHE, - ':snowballrun:processDebugManifest': FROM_CACHE, - ':snowballrun:processDebugManifestForPackage': FROM_CACHE, - ':snowballrun:processDebugResources': SUCCESS, - ':snowballrun:processManifestDebugForFeature': FROM_CACHE, - ':snowballrun:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:assembleDebug': SUCCESS, - ':tracker:bundleDebugAar': SUCCESS, - ':tracker:bundleLibCompileToJarDebug': FROM_CACHE, - ':tracker:bundleLibResDebug': FROM_CACHE, - ':tracker:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':tracker:compileDebugAidl': NO_SOURCE, - ':tracker:compileDebugJavaWithJavac': SUCCESS, - ':tracker:compileDebugKotlin': FROM_CACHE, - ':tracker:compileDebugLibraryResources': SUCCESS, - ':tracker:compileDebugRenderscript': NO_SOURCE, - ':tracker:compileDebugShaders': NO_SOURCE, - ':tracker:compileDebugSources': SUCCESS, - ':tracker:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':tracker:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':tracker:createFullJarDebug': FROM_CACHE, - ':tracker:extractDebugAnnotations': FROM_CACHE, - ':tracker:extractDeepLinksDebug': FROM_CACHE, - ':tracker:generateDebugAssets': UP_TO_DATE, - ':tracker:generateDebugBuildConfig': FROM_CACHE, - ':tracker:generateDebugRFile': FROM_CACHE, - ':tracker:generateDebugResValues': FROM_CACHE, - ':tracker:generateDebugResources': UP_TO_DATE, - ':tracker:kaptDebugKotlin': FROM_CACHE, - ':tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':tracker:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':tracker:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':tracker:mergeDebugJavaResource': FROM_CACHE, - ':tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':tracker:mergeDebugNativeLibs': NO_SOURCE, - ':tracker:mergeDebugResources': FROM_CACHE, - ':tracker:mergeDebugShaders': FROM_CACHE, - ':tracker:packageDebugAssets': FROM_CACHE, - ':tracker:packageDebugRenderscript': NO_SOURCE, - ':tracker:packageDebugResources': FROM_CACHE, - ':tracker:parseDebugLocalResources': FROM_CACHE, - ':tracker:preBuild': UP_TO_DATE, - ':tracker:preDebugBuild': UP_TO_DATE, - ':tracker:prepareLintJarForPublish': SUCCESS, - ':tracker:processDebugJavaRes': NO_SOURCE, - ':tracker:processDebugManifest': FROM_CACHE, - ':tracker:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:syncDebugLibJars': FROM_CACHE, - ':tracker:writeDebugAarMetadata': FROM_CACHE, - ':wearable:assembleDebug': SUCCESS, - ':wearable:checkDebugAarMetadata': FROM_CACHE, - ':wearable:checkDebugDuplicateClasses': FROM_CACHE, - ':wearable:compileDebugAidl': NO_SOURCE, - ':wearable:compileDebugJavaWithJavac': FROM_CACHE, - ':wearable:compileDebugKotlin': FROM_CACHE, - ':wearable:compileDebugRenderscript': NO_SOURCE, - ':wearable:compileDebugShaders': NO_SOURCE, - ':wearable:compileDebugSources': UP_TO_DATE, - ':wearable:compressDebugAssets': FROM_CACHE, - ':wearable:createDebugCompatibleScreenManifests': FROM_CACHE, - ':wearable:desugarDebugFileDependencies': SUCCESS, - ':wearable:dexBuilderDebug': FROM_CACHE, - ':wearable:extractDeepLinksDebug': FROM_CACHE, - ':wearable:generateDebugAssets': UP_TO_DATE, - ':wearable:generateDebugBuildConfig': FROM_CACHE, - ':wearable:generateDebugResValues': FROM_CACHE, - ':wearable:generateDebugResources': UP_TO_DATE, - ':wearable:kaptDebugKotlin': SKIPPED, - ':wearable:kaptGenerateStubsDebugKotlin': SKIPPED, - ':wearable:mergeDebugAssets': FROM_CACHE, - ':wearable:mergeDebugJavaResource': FROM_CACHE, - ':wearable:mergeDebugJniLibFolders': FROM_CACHE, - ':wearable:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':wearable:mergeDebugNativeLibs': FROM_CACHE, - ':wearable:mergeDebugResources': SUCCESS, - ':wearable:mergeDebugShaders': FROM_CACHE, - ':wearable:mergeExtDexDebug': FROM_CACHE, - ':wearable:mergeLibDexDebug': FROM_CACHE, - ':wearable:mergeProjectDexDebug': FROM_CACHE, - ':wearable:packageDebug': SUCCESS, - ':wearable:preBuild': UP_TO_DATE, - ':wearable:preDebugBuild': UP_TO_DATE, - ':wearable:processDebugJavaRes': NO_SOURCE, - ':wearable:processDebugMainManifest': FROM_CACHE, - ':wearable:processDebugManifest': FROM_CACHE, - ':wearable:processDebugManifestForPackage': FROM_CACHE, - ':wearable:processDebugResources': SUCCESS, - ':wearable:stripDebugDebugSymbols': NO_SOURCE, - ':wearable:validateSigningDebug': FROM_CACHE, - ':wearable:writeDebugAppMetadata': FROM_CACHE, - ':wearable:writeDebugSigningConfigVersions': FROM_CACHE, - ] - - static final EXPECTED_RESULTS_4_1 = [ - ':cityquiz:assembleDebug': SUCCESS, - ':cityquiz:checkDebugAarMetadata': FROM_CACHE, - ':cityquiz:checkDebugDuplicateClasses': FROM_CACHE, - ':cityquiz:compileDebugAidl': NO_SOURCE, - ':cityquiz:compileDebugJavaWithJavac': FROM_CACHE, - ':cityquiz:compileDebugKotlin': FROM_CACHE, - ':cityquiz:compileDebugRenderscript': NO_SOURCE, - ':cityquiz:compileDebugShaders': NO_SOURCE, - ':cityquiz:compileDebugSources': UP_TO_DATE, - ':cityquiz:compressDebugAssets': FROM_CACHE, - ':cityquiz:createDebugCompatibleScreenManifests': FROM_CACHE, - ':cityquiz:desugarDebugFileDependencies': SUCCESS, - ':cityquiz:dexBuilderDebug': FROM_CACHE, - ':cityquiz:extractDeepLinksDebug': FROM_CACHE, - ':cityquiz:featureDebugWriter': SUCCESS, - ':cityquiz:generateDebugAssets': UP_TO_DATE, - ':cityquiz:generateDebugBuildConfig': FROM_CACHE, - ':cityquiz:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':cityquiz:generateDebugResValues': FROM_CACHE, - ':cityquiz:generateDebugResources': UP_TO_DATE, - ':cityquiz:javaPreCompileDebug': FROM_CACHE, - ':cityquiz:mergeDebugAssets': FROM_CACHE, - ':cityquiz:mergeDebugJavaResource': FROM_CACHE, - ':cityquiz:mergeDebugJniLibFolders': FROM_CACHE, - ':cityquiz:mergeDebugNativeLibs': NO_SOURCE, - ':cityquiz:mergeDebugResources': SUCCESS, - ':cityquiz:mergeDebugShaders': FROM_CACHE, - ':cityquiz:mergeExtDexDebug': FROM_CACHE, - ':cityquiz:mergeLibDexDebug': FROM_CACHE, - ':cityquiz:mergeProjectDexDebug': FROM_CACHE, - ':cityquiz:packageDebug': SUCCESS, - ':cityquiz:preBuild': UP_TO_DATE, - ':cityquiz:preDebugBuild': UP_TO_DATE, - ':cityquiz:processApplicationManifestDebugForBundle': FROM_CACHE, - ':cityquiz:processDebugJavaRes': NO_SOURCE, - ':cityquiz:processDebugMainManifest': FROM_CACHE, - ':cityquiz:processDebugManifest': FROM_CACHE, - ':cityquiz:processDebugManifestForPackage': FROM_CACHE, - ':cityquiz:processDebugResources': SUCCESS, - ':cityquiz:processManifestDebugForFeature': FROM_CACHE, - ':cityquiz:stripDebugDebugSymbols': NO_SOURCE, - ':common:assembleDebug': SUCCESS, - ':common:bundleDebugAar': SUCCESS, - ':common:bundleLibCompileToJarDebug': FROM_CACHE, - ':common:bundleLibResDebug': FROM_CACHE, - ':common:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':common:compileDebugAidl': NO_SOURCE, - ':common:compileDebugJavaWithJavac': FROM_CACHE, - ':common:compileDebugKotlin': FROM_CACHE, - ':common:compileDebugLibraryResources': SUCCESS, - ':common:compileDebugRenderscript': NO_SOURCE, - ':common:compileDebugShaders': NO_SOURCE, - ':common:compileDebugSources': UP_TO_DATE, - ':common:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':common:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':common:createFullJarDebug': FROM_CACHE, - ':common:extractDebugAnnotations': FROM_CACHE, - ':common:extractDeepLinksDebug': FROM_CACHE, - ':common:generateDebugAssets': UP_TO_DATE, - ':common:generateDebugBuildConfig': FROM_CACHE, - ':common:generateDebugRFile': FROM_CACHE, - ':common:generateDebugResValues': FROM_CACHE, - ':common:generateDebugResources': UP_TO_DATE, - ':common:javaPreCompileDebug': FROM_CACHE, - ':common:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':common:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':common:mergeDebugJavaResource': FROM_CACHE, - ':common:mergeDebugJniLibFolders': FROM_CACHE, - ':common:mergeDebugNativeLibs': NO_SOURCE, - ':common:mergeDebugShaders': FROM_CACHE, - ':common:packageDebugAssets': FROM_CACHE, - ':common:packageDebugRenderscript': NO_SOURCE, - ':common:packageDebugResources': FROM_CACHE, - ':common:parseDebugLocalResources': FROM_CACHE, - ':common:preBuild': UP_TO_DATE, - ':common:preDebugBuild': UP_TO_DATE, - ':common:prepareLintJarForPublish': SUCCESS, - ':common:processDebugJavaRes': NO_SOURCE, - ':common:processDebugManifest': FROM_CACHE, - ':common:stripDebugDebugSymbols': NO_SOURCE, - ':common:syncDebugLibJars': FROM_CACHE, - ':common:writeDebugAarMetadata': FROM_CACHE, - ':dasherdancer:assembleDebug': SUCCESS, - ':dasherdancer:checkDebugAarMetadata': FROM_CACHE, - ':dasherdancer:checkDebugDuplicateClasses': FROM_CACHE, - ':dasherdancer:compileDebugAidl': NO_SOURCE, - ':dasherdancer:compileDebugJavaWithJavac': FROM_CACHE, - ':dasherdancer:compileDebugKotlin': FROM_CACHE, - ':dasherdancer:compileDebugRenderscript': NO_SOURCE, - ':dasherdancer:compileDebugShaders': NO_SOURCE, - ':dasherdancer:compileDebugSources': UP_TO_DATE, - ':dasherdancer:compressDebugAssets': FROM_CACHE, - ':dasherdancer:createDebugCompatibleScreenManifests': FROM_CACHE, - ':dasherdancer:desugarDebugFileDependencies': SUCCESS, - ':dasherdancer:dexBuilderDebug': FROM_CACHE, - ':dasherdancer:extractDeepLinksDebug': FROM_CACHE, - ':dasherdancer:featureDebugWriter': SUCCESS, - ':dasherdancer:generateDebugAssets': UP_TO_DATE, - ':dasherdancer:generateDebugBuildConfig': FROM_CACHE, - ':dasherdancer:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':dasherdancer:generateDebugResValues': FROM_CACHE, - ':dasherdancer:generateDebugResources': UP_TO_DATE, - ':dasherdancer:javaPreCompileDebug': FROM_CACHE, - ':dasherdancer:mergeDebugAssets': FROM_CACHE, - ':dasherdancer:mergeDebugJavaResource': FROM_CACHE, - ':dasherdancer:mergeDebugJniLibFolders': FROM_CACHE, - ':dasherdancer:mergeDebugNativeLibs': NO_SOURCE, - ':dasherdancer:mergeDebugResources': SUCCESS, - ':dasherdancer:mergeDebugShaders': FROM_CACHE, - ':dasherdancer:mergeExtDexDebug': FROM_CACHE, - ':dasherdancer:mergeLibDexDebug': FROM_CACHE, - ':dasherdancer:mergeProjectDexDebug': FROM_CACHE, - ':dasherdancer:packageDebug': SUCCESS, - ':dasherdancer:preBuild': UP_TO_DATE, - ':dasherdancer:preDebugBuild': UP_TO_DATE, - ':dasherdancer:processApplicationManifestDebugForBundle': FROM_CACHE, - ':dasherdancer:processDebugJavaRes': NO_SOURCE, - ':dasherdancer:processDebugMainManifest': FROM_CACHE, - ':dasherdancer:processDebugManifest': FROM_CACHE, - ':dasherdancer:processDebugManifestForPackage': FROM_CACHE, - ':dasherdancer:processDebugResources': SUCCESS, - ':dasherdancer:processManifestDebugForFeature': FROM_CACHE, - ':dasherdancer:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:assembleDebug': SUCCESS, - ':doodles-lib:bundleDebugAar': SUCCESS, - ':doodles-lib:bundleLibCompileToJarDebug': FROM_CACHE, - ':doodles-lib:bundleLibResDebug': NO_SOURCE, - ':doodles-lib:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':doodles-lib:compileDebugAidl': NO_SOURCE, - ':doodles-lib:compileDebugJavaWithJavac': FROM_CACHE, - ':doodles-lib:compileDebugLibraryResources': SUCCESS, - ':doodles-lib:compileDebugRenderscript': NO_SOURCE, - ':doodles-lib:compileDebugShaders': NO_SOURCE, - ':doodles-lib:compileDebugSources': UP_TO_DATE, - ':doodles-lib:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':doodles-lib:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':doodles-lib:createFullJarDebug': FROM_CACHE, - ':doodles-lib:extractDebugAnnotations': FROM_CACHE, - ':doodles-lib:extractDeepLinksDebug': FROM_CACHE, - ':doodles-lib:generateDebugAssets': UP_TO_DATE, - ':doodles-lib:generateDebugBuildConfig': FROM_CACHE, - ':doodles-lib:generateDebugRFile': FROM_CACHE, - ':doodles-lib:generateDebugResValues': FROM_CACHE, - ':doodles-lib:generateDebugResources': UP_TO_DATE, - ':doodles-lib:javaPreCompileDebug': FROM_CACHE, - ':doodles-lib:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':doodles-lib:mergeDebugJavaResource': FROM_CACHE, - ':doodles-lib:mergeDebugJniLibFolders': FROM_CACHE, - ':doodles-lib:mergeDebugNativeLibs': NO_SOURCE, - ':doodles-lib:mergeDebugShaders': FROM_CACHE, - ':doodles-lib:packageDebugAssets': FROM_CACHE, - ':doodles-lib:packageDebugRenderscript': NO_SOURCE, - ':doodles-lib:packageDebugResources': FROM_CACHE, - ':doodles-lib:parseDebugLocalResources': FROM_CACHE, - ':doodles-lib:preBuild': UP_TO_DATE, - ':doodles-lib:preDebugBuild': UP_TO_DATE, - ':doodles-lib:prepareLintJarForPublish': SUCCESS, - ':doodles-lib:processDebugJavaRes': NO_SOURCE, - ':doodles-lib:processDebugManifest': FROM_CACHE, - ':doodles-lib:stripDebugDebugSymbols': NO_SOURCE, - ':doodles-lib:syncDebugLibJars': FROM_CACHE, - ':doodles-lib:writeDebugAarMetadata': FROM_CACHE, - ':gumball:assembleDebug': SUCCESS, - ':gumball:checkDebugAarMetadata': FROM_CACHE, - ':gumball:checkDebugDuplicateClasses': FROM_CACHE, - ':gumball:compileDebugAidl': NO_SOURCE, - ':gumball:compileDebugJavaWithJavac': FROM_CACHE, - ':gumball:compileDebugRenderscript': NO_SOURCE, - ':gumball:compileDebugShaders': NO_SOURCE, - ':gumball:compileDebugSources': UP_TO_DATE, - ':gumball:compressDebugAssets': FROM_CACHE, - ':gumball:createDebugCompatibleScreenManifests': FROM_CACHE, - ':gumball:desugarDebugFileDependencies': SUCCESS, - ':gumball:dexBuilderDebug': FROM_CACHE, - ':gumball:extractDeepLinksDebug': FROM_CACHE, - ':gumball:featureDebugWriter': SUCCESS, - ':gumball:generateDebugAssets': UP_TO_DATE, - ':gumball:generateDebugBuildConfig': FROM_CACHE, - ':gumball:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':gumball:generateDebugResValues': FROM_CACHE, - ':gumball:generateDebugResources': UP_TO_DATE, - ':gumball:javaPreCompileDebug': FROM_CACHE, - ':gumball:mergeDebugAssets': FROM_CACHE, - ':gumball:mergeDebugJavaResource': FROM_CACHE, - ':gumball:mergeDebugJniLibFolders': FROM_CACHE, - ':gumball:mergeDebugNativeLibs': FROM_CACHE, - ':gumball:mergeDebugResources': SUCCESS, - ':gumball:mergeDebugShaders': FROM_CACHE, - ':gumball:mergeExtDexDebug': FROM_CACHE, - ':gumball:mergeLibDexDebug': FROM_CACHE, - ':gumball:mergeProjectDexDebug': FROM_CACHE, - ':gumball:packageDebug': SUCCESS, - ':gumball:preBuild': UP_TO_DATE, - ':gumball:preDebugBuild': UP_TO_DATE, - ':gumball:processApplicationManifestDebugForBundle': FROM_CACHE, - ':gumball:processDebugJavaRes': NO_SOURCE, - ':gumball:processDebugMainManifest': FROM_CACHE, - ':gumball:processDebugManifest': FROM_CACHE, - ':gumball:processDebugManifestForPackage': FROM_CACHE, - ':gumball:processDebugResources': SUCCESS, - ':gumball:processManifestDebugForFeature': FROM_CACHE, - ':gumball:stripDebugDebugSymbols': NO_SOURCE, - ':jetpack:assembleDebug': SUCCESS, - ':jetpack:checkDebugAarMetadata': FROM_CACHE, - ':jetpack:checkDebugDuplicateClasses': FROM_CACHE, - ':jetpack:compileDebugAidl': NO_SOURCE, - ':jetpack:compileDebugJavaWithJavac': FROM_CACHE, - ':jetpack:compileDebugKotlin': FROM_CACHE, - ':jetpack:compileDebugRenderscript': NO_SOURCE, - ':jetpack:compileDebugShaders': NO_SOURCE, - ':jetpack:compileDebugSources': UP_TO_DATE, - ':jetpack:compressDebugAssets': FROM_CACHE, - ':jetpack:createDebugCompatibleScreenManifests': FROM_CACHE, - ':jetpack:desugarDebugFileDependencies': SUCCESS, - ':jetpack:dexBuilderDebug': FROM_CACHE, - ':jetpack:extractDeepLinksDebug': FROM_CACHE, - ':jetpack:featureDebugWriter': SUCCESS, - ':jetpack:generateDebugAssets': UP_TO_DATE, - ':jetpack:generateDebugBuildConfig': FROM_CACHE, - ':jetpack:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':jetpack:generateDebugResValues': FROM_CACHE, - ':jetpack:generateDebugResources': UP_TO_DATE, - ':jetpack:javaPreCompileDebug': FROM_CACHE, - ':jetpack:mergeDebugAssets': FROM_CACHE, - ':jetpack:mergeDebugJavaResource': FROM_CACHE, - ':jetpack:mergeDebugJniLibFolders': FROM_CACHE, - ':jetpack:mergeDebugNativeLibs': NO_SOURCE, - ':jetpack:mergeDebugResources': SUCCESS, - ':jetpack:mergeDebugShaders': FROM_CACHE, - ':jetpack:mergeExtDexDebug': FROM_CACHE, - ':jetpack:mergeLibDexDebug': FROM_CACHE, - ':jetpack:mergeProjectDexDebug': FROM_CACHE, - ':jetpack:packageDebug': SUCCESS, - ':jetpack:preBuild': UP_TO_DATE, - ':jetpack:preDebugBuild': UP_TO_DATE, - ':jetpack:processApplicationManifestDebugForBundle': FROM_CACHE, - ':jetpack:processDebugJavaRes': NO_SOURCE, - ':jetpack:processDebugMainManifest': FROM_CACHE, - ':jetpack:processDebugManifest': FROM_CACHE, - ':jetpack:processDebugManifestForPackage': FROM_CACHE, - ':jetpack:processDebugResources': SUCCESS, - ':jetpack:processManifestDebugForFeature': FROM_CACHE, - ':jetpack:stripDebugDebugSymbols': NO_SOURCE, - ':memory:assembleDebug': SUCCESS, - ':memory:checkDebugAarMetadata': FROM_CACHE, - ':memory:checkDebugDuplicateClasses': FROM_CACHE, - ':memory:compileDebugAidl': NO_SOURCE, - ':memory:compileDebugJavaWithJavac': FROM_CACHE, - ':memory:compileDebugRenderscript': NO_SOURCE, - ':memory:compileDebugShaders': NO_SOURCE, - ':memory:compileDebugSources': UP_TO_DATE, - ':memory:compressDebugAssets': FROM_CACHE, - ':memory:createDebugCompatibleScreenManifests': FROM_CACHE, - ':memory:desugarDebugFileDependencies': SUCCESS, - ':memory:dexBuilderDebug': FROM_CACHE, - ':memory:extractDeepLinksDebug': FROM_CACHE, - ':memory:featureDebugWriter': SUCCESS, - ':memory:generateDebugAssets': UP_TO_DATE, - ':memory:generateDebugBuildConfig': FROM_CACHE, - ':memory:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':memory:generateDebugResValues': FROM_CACHE, - ':memory:generateDebugResources': UP_TO_DATE, - ':memory:javaPreCompileDebug': FROM_CACHE, - ':memory:mergeDebugAssets': FROM_CACHE, - ':memory:mergeDebugJavaResource': FROM_CACHE, - ':memory:mergeDebugJniLibFolders': FROM_CACHE, - ':memory:mergeDebugNativeLibs': NO_SOURCE, - ':memory:mergeDebugResources': SUCCESS, - ':memory:mergeDebugShaders': FROM_CACHE, - ':memory:mergeExtDexDebug': FROM_CACHE, - ':memory:mergeLibDexDebug': FROM_CACHE, - ':memory:mergeProjectDexDebug': FROM_CACHE, - ':memory:packageDebug': SUCCESS, - ':memory:preBuild': UP_TO_DATE, - ':memory:preDebugBuild': UP_TO_DATE, - ':memory:processApplicationManifestDebugForBundle': FROM_CACHE, - ':memory:processDebugJavaRes': NO_SOURCE, - ':memory:processDebugMainManifest': FROM_CACHE, - ':memory:processDebugManifest': FROM_CACHE, - ':memory:processDebugManifestForPackage': FROM_CACHE, - ':memory:processDebugResources': SUCCESS, - ':memory:processManifestDebugForFeature': FROM_CACHE, - ':memory:stripDebugDebugSymbols': NO_SOURCE, - ':penguinswim:assembleDebug': SUCCESS, - ':penguinswim:checkDebugAarMetadata': FROM_CACHE, - ':penguinswim:checkDebugDuplicateClasses': FROM_CACHE, - ':penguinswim:compileDebugAidl': NO_SOURCE, - ':penguinswim:compileDebugJavaWithJavac': FROM_CACHE, - ':penguinswim:compileDebugRenderscript': NO_SOURCE, - ':penguinswim:compileDebugShaders': NO_SOURCE, - ':penguinswim:compileDebugSources': UP_TO_DATE, - ':penguinswim:compressDebugAssets': FROM_CACHE, - ':penguinswim:createDebugCompatibleScreenManifests': FROM_CACHE, - ':penguinswim:desugarDebugFileDependencies': SUCCESS, - ':penguinswim:dexBuilderDebug': FROM_CACHE, - ':penguinswim:extractDeepLinksDebug': FROM_CACHE, - ':penguinswim:featureDebugWriter': SUCCESS, - ':penguinswim:generateDebugAssets': UP_TO_DATE, - ':penguinswim:generateDebugBuildConfig': FROM_CACHE, - ':penguinswim:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':penguinswim:generateDebugResValues': FROM_CACHE, - ':penguinswim:generateDebugResources': UP_TO_DATE, - ':penguinswim:javaPreCompileDebug': FROM_CACHE, - ':penguinswim:mergeDebugAssets': FROM_CACHE, - ':penguinswim:mergeDebugJavaResource': FROM_CACHE, - ':penguinswim:mergeDebugJniLibFolders': FROM_CACHE, - ':penguinswim:mergeDebugNativeLibs': NO_SOURCE, - ':penguinswim:mergeDebugResources': SUCCESS, - ':penguinswim:mergeDebugShaders': FROM_CACHE, - ':penguinswim:mergeExtDexDebug': FROM_CACHE, - ':penguinswim:mergeLibDexDebug': FROM_CACHE, - ':penguinswim:mergeProjectDexDebug': FROM_CACHE, - ':penguinswim:packageDebug': SUCCESS, - ':penguinswim:preBuild': UP_TO_DATE, - ':penguinswim:preDebugBuild': UP_TO_DATE, - ':penguinswim:processApplicationManifestDebugForBundle': FROM_CACHE, - ':penguinswim:processDebugJavaRes': NO_SOURCE, - ':penguinswim:processDebugMainManifest': FROM_CACHE, - ':penguinswim:processDebugManifest': FROM_CACHE, - ':penguinswim:processDebugManifestForPackage': FROM_CACHE, - ':penguinswim:processDebugResources': SUCCESS, - ':penguinswim:processManifestDebugForFeature': FROM_CACHE, - ':penguinswim:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:assembleDebug': SUCCESS, - ':playgames:bundleDebugAar': SUCCESS, - ':playgames:bundleLibCompileToJarDebug': FROM_CACHE, - ':playgames:bundleLibResDebug': NO_SOURCE, - ':playgames:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':playgames:compileDebugAidl': NO_SOURCE, - ':playgames:compileDebugJavaWithJavac': FROM_CACHE, - ':playgames:compileDebugLibraryResources': SUCCESS, - ':playgames:compileDebugRenderscript': NO_SOURCE, - ':playgames:compileDebugShaders': NO_SOURCE, - ':playgames:compileDebugSources': UP_TO_DATE, - ':playgames:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':playgames:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':playgames:createFullJarDebug': FROM_CACHE, - ':playgames:extractDebugAnnotations': FROM_CACHE, - ':playgames:extractDeepLinksDebug': FROM_CACHE, - ':playgames:generateDebugAssets': UP_TO_DATE, - ':playgames:generateDebugBuildConfig': FROM_CACHE, - ':playgames:generateDebugRFile': FROM_CACHE, - ':playgames:generateDebugResValues': FROM_CACHE, - ':playgames:generateDebugResources': UP_TO_DATE, - ':playgames:javaPreCompileDebug': FROM_CACHE, - ':playgames:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':playgames:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':playgames:mergeDebugJavaResource': FROM_CACHE, - ':playgames:mergeDebugJniLibFolders': FROM_CACHE, - ':playgames:mergeDebugNativeLibs': NO_SOURCE, - ':playgames:mergeDebugShaders': FROM_CACHE, - ':playgames:packageDebugAssets': FROM_CACHE, - ':playgames:packageDebugRenderscript': NO_SOURCE, - ':playgames:packageDebugResources': FROM_CACHE, - ':playgames:parseDebugLocalResources': FROM_CACHE, - ':playgames:preBuild': UP_TO_DATE, - ':playgames:preDebugBuild': UP_TO_DATE, - ':playgames:prepareLintJarForPublish': SUCCESS, - ':playgames:processDebugJavaRes': NO_SOURCE, - ':playgames:processDebugManifest': FROM_CACHE, - ':playgames:stripDebugDebugSymbols': NO_SOURCE, - ':playgames:syncDebugLibJars': FROM_CACHE, - ':playgames:writeDebugAarMetadata': FROM_CACHE, - ':presenttoss:assembleDebug': SUCCESS, - ':presenttoss:checkDebugAarMetadata': FROM_CACHE, - ':presenttoss:checkDebugDuplicateClasses': FROM_CACHE, - ':presenttoss:compileDebugAidl': NO_SOURCE, - ':presenttoss:compileDebugJavaWithJavac': FROM_CACHE, - ':presenttoss:compileDebugRenderscript': NO_SOURCE, - ':presenttoss:compileDebugShaders': NO_SOURCE, - ':presenttoss:compileDebugSources': UP_TO_DATE, - ':presenttoss:compressDebugAssets': FROM_CACHE, - ':presenttoss:createDebugCompatibleScreenManifests': FROM_CACHE, - ':presenttoss:desugarDebugFileDependencies': SUCCESS, - ':presenttoss:dexBuilderDebug': FROM_CACHE, - ':presenttoss:extractDeepLinksDebug': FROM_CACHE, - ':presenttoss:featureDebugWriter': SUCCESS, - ':presenttoss:generateDebugAssets': UP_TO_DATE, - ':presenttoss:generateDebugBuildConfig': FROM_CACHE, - ':presenttoss:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':presenttoss:generateDebugResValues': FROM_CACHE, - ':presenttoss:generateDebugResources': UP_TO_DATE, - ':presenttoss:javaPreCompileDebug': FROM_CACHE, - ':presenttoss:mergeDebugAssets': FROM_CACHE, - ':presenttoss:mergeDebugJavaResource': FROM_CACHE, - ':presenttoss:mergeDebugJniLibFolders': FROM_CACHE, - ':presenttoss:mergeDebugNativeLibs': NO_SOURCE, - ':presenttoss:mergeDebugResources': SUCCESS, - ':presenttoss:mergeDebugShaders': FROM_CACHE, - ':presenttoss:mergeExtDexDebug': FROM_CACHE, - ':presenttoss:mergeLibDexDebug': FROM_CACHE, - ':presenttoss:mergeProjectDexDebug': FROM_CACHE, - ':presenttoss:packageDebug': SUCCESS, - ':presenttoss:preBuild': UP_TO_DATE, - ':presenttoss:preDebugBuild': UP_TO_DATE, - ':presenttoss:processApplicationManifestDebugForBundle': FROM_CACHE, - ':presenttoss:processDebugJavaRes': NO_SOURCE, - ':presenttoss:processDebugMainManifest': FROM_CACHE, - ':presenttoss:processDebugManifest': FROM_CACHE, - ':presenttoss:processDebugManifestForPackage': FROM_CACHE, - ':presenttoss:processDebugResources': SUCCESS, - ':presenttoss:processManifestDebugForFeature': FROM_CACHE, - ':presenttoss:stripDebugDebugSymbols': NO_SOURCE, - ':rocketsleigh:assembleDebug': SUCCESS, - ':rocketsleigh:checkDebugAarMetadata': FROM_CACHE, - ':rocketsleigh:checkDebugDuplicateClasses': FROM_CACHE, - ':rocketsleigh:compileDebugAidl': NO_SOURCE, - ':rocketsleigh:compileDebugJavaWithJavac': FROM_CACHE, - ':rocketsleigh:compileDebugKotlin': FROM_CACHE, - ':rocketsleigh:compileDebugRenderscript': NO_SOURCE, - ':rocketsleigh:compileDebugShaders': NO_SOURCE, - ':rocketsleigh:compileDebugSources': UP_TO_DATE, - ':rocketsleigh:compressDebugAssets': FROM_CACHE, - ':rocketsleigh:createDebugCompatibleScreenManifests': FROM_CACHE, - ':rocketsleigh:desugarDebugFileDependencies': SUCCESS, - ':rocketsleigh:dexBuilderDebug': FROM_CACHE, - ':rocketsleigh:extractDeepLinksDebug': FROM_CACHE, - ':rocketsleigh:featureDebugWriter': SUCCESS, - ':rocketsleigh:generateDebugAssets': UP_TO_DATE, - ':rocketsleigh:generateDebugBuildConfig': FROM_CACHE, - ':rocketsleigh:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':rocketsleigh:generateDebugResValues': FROM_CACHE, - ':rocketsleigh:generateDebugResources': UP_TO_DATE, - ':rocketsleigh:javaPreCompileDebug': FROM_CACHE, - ':rocketsleigh:mergeDebugAssets': FROM_CACHE, - ':rocketsleigh:mergeDebugJavaResource': FROM_CACHE, - ':rocketsleigh:mergeDebugJniLibFolders': FROM_CACHE, - ':rocketsleigh:mergeDebugNativeLibs': NO_SOURCE, - ':rocketsleigh:mergeDebugResources': SUCCESS, - ':rocketsleigh:mergeDebugShaders': FROM_CACHE, - ':rocketsleigh:mergeExtDexDebug': FROM_CACHE, - ':rocketsleigh:mergeLibDexDebug': FROM_CACHE, - ':rocketsleigh:mergeProjectDexDebug': FROM_CACHE, - ':rocketsleigh:packageDebug': SUCCESS, - ':rocketsleigh:preBuild': UP_TO_DATE, - ':rocketsleigh:preDebugBuild': UP_TO_DATE, - ':rocketsleigh:processApplicationManifestDebugForBundle': FROM_CACHE, - ':rocketsleigh:processDebugJavaRes': NO_SOURCE, - ':rocketsleigh:processDebugMainManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifest': FROM_CACHE, - ':rocketsleigh:processDebugManifestForPackage': FROM_CACHE, - ':rocketsleigh:processDebugResources': SUCCESS, - ':rocketsleigh:processManifestDebugForFeature': FROM_CACHE, - ':rocketsleigh:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:assembleDebug': SUCCESS, - ':santa-tracker:bundleDebugClasses': FROM_CACHE, - ':santa-tracker:checkDebugAarMetadata': FROM_CACHE, - ':santa-tracker:checkDebugDuplicateClasses': FROM_CACHE, - ':santa-tracker:checkDebugLibraries': FROM_CACHE, - ':santa-tracker:compileDebugAidl': NO_SOURCE, - ':santa-tracker:compileDebugJavaWithJavac': FROM_CACHE, - ':santa-tracker:compileDebugKotlin': FROM_CACHE, - ':santa-tracker:compileDebugRenderscript': NO_SOURCE, - ':santa-tracker:compileDebugShaders': NO_SOURCE, - ':santa-tracker:compileDebugSources': UP_TO_DATE, - ':santa-tracker:compressDebugAssets': FROM_CACHE, - ':santa-tracker:createDebugCompatibleScreenManifests': FROM_CACHE, - ':santa-tracker:desugarDebugFileDependencies': SUCCESS, - ':santa-tracker:dexBuilderDebug': FROM_CACHE, - ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, - ':santa-tracker:generateDebugAssets': UP_TO_DATE, - ':santa-tracker:generateDebugBuildConfig': FROM_CACHE, - ':santa-tracker:generateDebugFeatureMetadata': FROM_CACHE, - ':santa-tracker:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':santa-tracker:generateDebugResValues': FROM_CACHE, - ':santa-tracker:generateDebugResources': UP_TO_DATE, - ':santa-tracker:handleDebugMicroApk': FROM_CACHE, - ':santa-tracker:javaPreCompileDebug': FROM_CACHE, - ':santa-tracker:kaptDebugKotlin': FROM_CACHE, - ':santa-tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':santa-tracker:mergeDebugAssets': FROM_CACHE, - ':santa-tracker:mergeDebugJavaResource': FROM_CACHE, - ':santa-tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':santa-tracker:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':santa-tracker:mergeDebugNativeLibs': FROM_CACHE, - ':santa-tracker:mergeDebugResources': SUCCESS, - ':santa-tracker:mergeDebugShaders': FROM_CACHE, - ':santa-tracker:mergeExtDexDebug': FROM_CACHE, - ':santa-tracker:mergeLibDexDebug': FROM_CACHE, - ':santa-tracker:mergeProjectDexDebug': FROM_CACHE, - ':santa-tracker:packageDebug': SUCCESS, - ':santa-tracker:preBuild': UP_TO_DATE, - ':santa-tracker:preDebugBuild': FROM_CACHE, - ':santa-tracker:processDebugJavaRes': NO_SOURCE, - ':santa-tracker:processDebugMainManifest': FROM_CACHE, - ':santa-tracker:processDebugManifest': FROM_CACHE, - ':santa-tracker:processDebugManifestForPackage': FROM_CACHE, - ':santa-tracker:processDebugResources': SUCCESS, - ':santa-tracker:signingConfigWriterDebug': FROM_CACHE, - ':santa-tracker:stripDebugDebugSymbols': NO_SOURCE, - ':santa-tracker:validateSigningDebug': FROM_CACHE, - ':santa-tracker:writeDebugModuleMetadata': SUCCESS, - ':snowballrun:assembleDebug': SUCCESS, - ':snowballrun:checkDebugAarMetadata': FROM_CACHE, - ':snowballrun:checkDebugDuplicateClasses': FROM_CACHE, - ':snowballrun:compileDebugAidl': NO_SOURCE, - ':snowballrun:compileDebugJavaWithJavac': FROM_CACHE, - ':snowballrun:compileDebugRenderscript': NO_SOURCE, - ':snowballrun:compileDebugShaders': NO_SOURCE, - ':snowballrun:compileDebugSources': UP_TO_DATE, - ':snowballrun:compressDebugAssets': FROM_CACHE, - ':snowballrun:createDebugCompatibleScreenManifests': FROM_CACHE, - ':snowballrun:desugarDebugFileDependencies': SUCCESS, - ':snowballrun:dexBuilderDebug': FROM_CACHE, - ':snowballrun:extractDeepLinksDebug': FROM_CACHE, - ':snowballrun:featureDebugWriter': SUCCESS, - ':snowballrun:generateDebugAssets': UP_TO_DATE, - ':snowballrun:generateDebugBuildConfig': FROM_CACHE, - ':snowballrun:generateDebugFeatureTransitiveDeps': FROM_CACHE, - ':snowballrun:generateDebugResValues': FROM_CACHE, - ':snowballrun:generateDebugResources': UP_TO_DATE, - ':snowballrun:javaPreCompileDebug': FROM_CACHE, - ':snowballrun:mergeDebugAssets': FROM_CACHE, - ':snowballrun:mergeDebugJavaResource': FROM_CACHE, - ':snowballrun:mergeDebugJniLibFolders': FROM_CACHE, - ':snowballrun:mergeDebugNativeLibs': NO_SOURCE, - ':snowballrun:mergeDebugResources': SUCCESS, - ':snowballrun:mergeDebugShaders': FROM_CACHE, - ':snowballrun:mergeExtDexDebug': FROM_CACHE, - ':snowballrun:mergeLibDexDebug': FROM_CACHE, - ':snowballrun:mergeProjectDexDebug': FROM_CACHE, - ':snowballrun:packageDebug': SUCCESS, - ':snowballrun:preBuild': UP_TO_DATE, - ':snowballrun:preDebugBuild': UP_TO_DATE, - ':snowballrun:processApplicationManifestDebugForBundle': FROM_CACHE, - ':snowballrun:processDebugJavaRes': NO_SOURCE, - ':snowballrun:processDebugMainManifest': FROM_CACHE, - ':snowballrun:processDebugManifest': FROM_CACHE, - ':snowballrun:processDebugManifestForPackage': FROM_CACHE, - ':snowballrun:processDebugResources': SUCCESS, - ':snowballrun:processManifestDebugForFeature': FROM_CACHE, - ':snowballrun:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:assembleDebug': SUCCESS, - ':tracker:bundleDebugAar': SUCCESS, - ':tracker:bundleLibCompileToJarDebug': FROM_CACHE, - ':tracker:bundleLibResDebug': FROM_CACHE, - ':tracker:bundleLibRuntimeToJarDebug': FROM_CACHE, - ':tracker:compileDebugAidl': NO_SOURCE, - ':tracker:compileDebugJavaWithJavac': SUCCESS, - ':tracker:compileDebugKotlin': FROM_CACHE, - ':tracker:compileDebugLibraryResources': SUCCESS, - ':tracker:compileDebugRenderscript': NO_SOURCE, - ':tracker:compileDebugShaders': NO_SOURCE, - ':tracker:compileDebugSources': SUCCESS, - ':tracker:copyDebugJniLibsProjectAndLocalJars': FROM_CACHE, - ':tracker:copyDebugJniLibsProjectOnly': FROM_CACHE, - ':tracker:createFullJarDebug': FROM_CACHE, - ':tracker:extractDebugAnnotations': FROM_CACHE, - ':tracker:extractDeepLinksDebug': FROM_CACHE, - ':tracker:generateDebugAssets': UP_TO_DATE, - ':tracker:generateDebugBuildConfig': FROM_CACHE, - ':tracker:generateDebugRFile': FROM_CACHE, - ':tracker:generateDebugResValues': FROM_CACHE, - ':tracker:generateDebugResources': UP_TO_DATE, - ':tracker:javaPreCompileDebug': FROM_CACHE, - ':tracker:kaptDebugKotlin': FROM_CACHE, - ':tracker:kaptGenerateStubsDebugKotlin': FROM_CACHE, - ':tracker:mergeDebugConsumerProguardFiles': FROM_CACHE, - ':tracker:mergeDebugGeneratedProguardFiles': FROM_CACHE, - ':tracker:mergeDebugJavaResource': FROM_CACHE, - ':tracker:mergeDebugJniLibFolders': FROM_CACHE, - ':tracker:mergeDebugNativeLibs': NO_SOURCE, - ':tracker:mergeDebugResources': FROM_CACHE, - ':tracker:mergeDebugShaders': FROM_CACHE, - ':tracker:packageDebugAssets': FROM_CACHE, - ':tracker:packageDebugRenderscript': NO_SOURCE, - ':tracker:packageDebugResources': FROM_CACHE, - ':tracker:parseDebugLocalResources': FROM_CACHE, - ':tracker:preBuild': UP_TO_DATE, - ':tracker:preDebugBuild': UP_TO_DATE, - ':tracker:prepareLintJarForPublish': SUCCESS, - ':tracker:processDebugJavaRes': NO_SOURCE, - ':tracker:processDebugManifest': FROM_CACHE, - ':tracker:stripDebugDebugSymbols': NO_SOURCE, - ':tracker:syncDebugLibJars': FROM_CACHE, - ':tracker:writeDebugAarMetadata': FROM_CACHE, - ':wearable:assembleDebug': SUCCESS, - ':wearable:checkDebugAarMetadata': FROM_CACHE, - ':wearable:checkDebugDuplicateClasses': FROM_CACHE, - ':wearable:compileDebugAidl': NO_SOURCE, - ':wearable:compileDebugJavaWithJavac': FROM_CACHE, - ':wearable:compileDebugKotlin': FROM_CACHE, - ':wearable:compileDebugRenderscript': NO_SOURCE, - ':wearable:compileDebugShaders': NO_SOURCE, - ':wearable:compileDebugSources': UP_TO_DATE, - ':wearable:compressDebugAssets': FROM_CACHE, - ':wearable:createDebugCompatibleScreenManifests': FROM_CACHE, - ':wearable:desugarDebugFileDependencies': SUCCESS, - ':wearable:dexBuilderDebug': FROM_CACHE, - ':wearable:extractDeepLinksDebug': FROM_CACHE, - ':wearable:generateDebugAssets': UP_TO_DATE, - ':wearable:generateDebugBuildConfig': FROM_CACHE, - ':wearable:generateDebugResValues': FROM_CACHE, - ':wearable:generateDebugResources': UP_TO_DATE, - ':wearable:javaPreCompileDebug': FROM_CACHE, - ':wearable:kaptDebugKotlin': SKIPPED, - ':wearable:kaptGenerateStubsDebugKotlin': SKIPPED, - ':wearable:mergeDebugAssets': FROM_CACHE, - ':wearable:mergeDebugJavaResource': FROM_CACHE, - ':wearable:mergeDebugJniLibFolders': FROM_CACHE, - ':wearable:mergeDebugNativeDebugMetadata': NO_SOURCE, - ':wearable:mergeDebugNativeLibs': FROM_CACHE, - ':wearable:mergeDebugResources': SUCCESS, - ':wearable:mergeDebugShaders': FROM_CACHE, - ':wearable:mergeExtDexDebug': FROM_CACHE, - ':wearable:mergeLibDexDebug': FROM_CACHE, - ':wearable:mergeProjectDexDebug': FROM_CACHE, - ':wearable:packageDebug': SUCCESS, - ':wearable:preBuild': UP_TO_DATE, - ':wearable:preDebugBuild': UP_TO_DATE, - ':wearable:processDebugJavaRes': NO_SOURCE, - ':wearable:processDebugMainManifest': FROM_CACHE, - ':wearable:processDebugManifest': FROM_CACHE, - ':wearable:processDebugManifestForPackage': FROM_CACHE, - ':wearable:processDebugResources': SUCCESS, - ':wearable:stripDebugDebugSymbols': NO_SOURCE, - ':wearable:validateSigningDebug': FROM_CACHE, - ] } From bcd2888c0777fb07cd964e46354b909341604f6b Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Sat, 10 Dec 2022 10:11:45 +0100 Subject: [PATCH 061/120] Update AndroidSantaTrackerCachingSmokeTest expectations for view binding Signed-off-by: Paul Merlin --- .../AndroidSantaTrackerCachingSmokeTest.groovy | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy index 0015145c0109..9f835c28a884 100644 --- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy +++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy @@ -136,6 +136,8 @@ class AndroidPluginExpectations8 { ':common:copyDebugJniLibsProjectAndLocalJars': SUCCESS, ':common:copyDebugJniLibsProjectOnly': SUCCESS, ':common:createFullJarDebug': FROM_CACHE, + ':common:dataBindingGenBaseClassesDebug': FROM_CACHE, + ':common:dataBindingMergeDependencyArtifactsDebug': SUCCESS, ':common:extractDebugAnnotations': FROM_CACHE, ':common:extractDeepLinksDebug': FROM_CACHE, ':common:extractDeepLinksForAarDebug': FROM_CACHE, @@ -533,6 +535,8 @@ class AndroidPluginExpectations8 { ':santa-tracker:compressDebugAssets': FROM_CACHE, ':santa-tracker:createDebugApkListingFileRedirect': SUCCESS, ':santa-tracker:createDebugCompatibleScreenManifests': SUCCESS, + ':santa-tracker:dataBindingGenBaseClassesDebug': FROM_CACHE, + ':santa-tracker:dataBindingMergeDependencyArtifactsDebug': SUCCESS, ':santa-tracker:desugarDebugFileDependencies': FROM_CACHE, ':santa-tracker:dexBuilderDebug': FROM_CACHE, ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, @@ -756,6 +760,8 @@ class AndroidPluginExpectations7 { ':common:copyDebugJniLibsProjectAndLocalJars': SUCCESS, ':common:copyDebugJniLibsProjectOnly': SUCCESS, ':common:createFullJarDebug': FROM_CACHE, + ':common:dataBindingGenBaseClassesDebug': FROM_CACHE, + ':common:dataBindingMergeDependencyArtifactsDebug': SUCCESS, ':common:extractDebugAnnotations': FROM_CACHE, ':common:extractDeepLinksDebug': FROM_CACHE, ':common:extractDeepLinksForAarDebug': FROM_CACHE, @@ -1170,6 +1176,8 @@ class AndroidPluginExpectations7 { ':santa-tracker:compressDebugAssets': FROM_CACHE, ':santa-tracker:createDebugApkListingFileRedirect': SUCCESS, ':santa-tracker:createDebugCompatibleScreenManifests': SUCCESS, + ':santa-tracker:dataBindingGenBaseClassesDebug': FROM_CACHE, + ':santa-tracker:dataBindingMergeDependencyArtifactsDebug': SUCCESS, ':santa-tracker:desugarDebugFileDependencies': FROM_CACHE, ':santa-tracker:dexBuilderDebug': FROM_CACHE, ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, @@ -1396,6 +1404,8 @@ class AndroidPluginExpectations7 { ':common:copyDebugJniLibsProjectAndLocalJars': SUCCESS, ':common:copyDebugJniLibsProjectOnly': SUCCESS, ':common:createFullJarDebug': FROM_CACHE, + ':common:dataBindingGenBaseClassesDebug': FROM_CACHE, + ':common:dataBindingMergeDependencyArtifactsDebug': SUCCESS, ':common:extractDebugAnnotations': FROM_CACHE, ':common:extractDeepLinksDebug': FROM_CACHE, ':common:extractDeepLinksForAarDebug': FROM_CACHE, @@ -1810,6 +1820,8 @@ class AndroidPluginExpectations7 { ':santa-tracker:compressDebugAssets': FROM_CACHE, ':santa-tracker:createDebugApkListingFileRedirect': SUCCESS, ':santa-tracker:createDebugCompatibleScreenManifests': SUCCESS, + ':santa-tracker:dataBindingGenBaseClassesDebug': FROM_CACHE, + ':santa-tracker:dataBindingMergeDependencyArtifactsDebug': SUCCESS, ':santa-tracker:desugarDebugFileDependencies': FROM_CACHE, ':santa-tracker:dexBuilderDebug': FROM_CACHE, ':santa-tracker:extractDeepLinksDebug': FROM_CACHE, From 45932963096f095c4651fc26a86578cfd68bde2d Mon Sep 17 00:00:00 2001 From: Paul Merlin Date: Sun, 11 Dec 2022 13:04:56 +0100 Subject: [PATCH 062/120] Bind santa-tracker remote with new git ref With: - https://github.com/gradle/santa-tracker-android/pull/21 - https://github.com/gradle/santa-tracker-android/pull/23 Signed-off-by: Paul Merlin --- .../src/main/groovy/gradlebuild.performance-templates.gradle | 2 +- subprojects/smoke-test/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/subprojects/smoke-test/build.gradle.kts b/subprojects/smoke-test/build.gradle.kts index bf05e413a7e2..286c8c994448 100644 --- a/subprojects/smoke-test/build.gradle.kts +++ b/subprojects/smoke-test/build.gradle.kts @@ -57,7 +57,7 @@ tasks { val santaTracker by registering(RemoteProject::class) { remoteUri.set(santaGitUri) // Pinned from branch main - ref.set("622fc64b7c39cb84e94174be3df9a54393348b45") + ref.set("37a7d12c40f36657a0cb0181979c401c752bd328") } val gradleBuildCurrent by registering(RemoteProject::class) { From adee45a6bdc9446881b2af5e5550546a84cedea8 Mon Sep 17 00:00:00 2001 From: Justin Van Dort Date: Fri, 9 Dec 2022 17:51:25 -0500 Subject: [PATCH 063/120] Update annotationProcessorGeneratedSourcesDirectory nag to 9.0 --- .../compile/JavaCompileIntegrationTest.groovy | 2 +- .../api/tasks/compile/CompileOptions.java | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) 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/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); } From aa5f6b51738b2b0e35ec2742482f4169472e34f9 Mon Sep 17 00:00:00 2001 From: Reinhold Degenfellner Date: Wed, 7 Dec 2022 11:12:43 +0100 Subject: [PATCH 064/120] fix missing upgrade docs 6.9 also align index with page content Resolves: #18957 Signed-off-by: Reinhold Degenfellner --- .../src/docs/userguide/migration/upgrading_version_6.adoc | 5 +++++ subprojects/docs/src/main/resources/header.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) 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/main/resources/header.html b/subprojects/docs/src/main/resources/header.html index e9798835f897..b6c398d12ff7 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
    • From c808e83a6d857ccb4f9e7ffffbec37229c643893 Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Tue, 6 Dec 2022 19:13:24 +0100 Subject: [PATCH 065/120] Group Java compatibility tests --- .../fixtures/AbstractIntegrationSpec.groovy | 1 + .../fixtures/jvm/JavaToolchainFixture.groovy | 9 + ...CompileCompatibilityIntegrationTest.groovy | 311 ++++++++++++++++++ ...JavaCompileToolchainIntegrationTest.groovy | 208 +----------- .../JavaBasePluginIntegrationTest.groovy | 119 +------ 5 files changed, 331 insertions(+), 317 deletions(-) create mode 100644 subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileCompatibilityIntegrationTest.groovy 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/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/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..f5051b6ec7b0 --- /dev/null +++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileCompatibilityIntegrationTest.groovy @@ -0,0 +1,311 @@ +/* + * 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.replace("", 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("")' | '' + "executable" | 'options.forkOptions.executable = ""' | OperatingSystem.current().getExecutableName('/bin/javac') + } + + 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}'.") + classJavaVersion(javaClassFile("Foo.class")) == jdk.javaVersion + } + + 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.doLast { + logger.lifecycle("project.sourceCompatibility = '\${project.java.sourceCompatibility}'") + logger.lifecycle("project.targetCompatibility = '\${project.java.targetCompatibility}'") + 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.doLast { + logger.lifecycle("project.sourceCompatibility = '\${project.java.sourceCompatibility}'") + logger.lifecycle("project.targetCompatibility = '\${project.java.targetCompatibility}'") + 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 "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.doLast { + logger.lifecycle("project.sourceCompatibility = '\${project.java.sourceCompatibility}'") + logger.lifecycle("project.targetCompatibility = '\${project.java.targetCompatibility}'") + 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'") + classJavaVersion(javaClassFile("Foo.class")) == javaVersion + } + + private static String currentJavaVersion() { + return Jvm.current().javaVersion.toString() + } +} 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/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() - } } From cf1e7e56cdf8c0a7911f18598243c922d88f855f Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Wed, 7 Dec 2022 12:41:06 +0100 Subject: [PATCH 066/120] Test access to language features and JDK APIs via compatibility options --- ...CompileCompatibilityIntegrationTest.groovy | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) 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 index f5051b6ec7b0..787f72db9c11 100644 --- 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 @@ -305,6 +305,270 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im classJavaVersion(javaClassFile("Foo.class")) == javaVersion } + def "source compatibility lower than compiler version does not allow accessing newer Java language features"() { + def jdk = AvailableJavaHomes.getJdk(toolchain) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.sourceCompatibility = "$sourceCompatibility" + """ + + file("src/main/java/Parent.java") << """ + // 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 {} + } + """ + + when: + if (fails) { + withInstallations(jdk).fails(":compileJava") + } else { + withInstallations(jdk).succeeds(":compileJava") + } + + then: + if (fails) { + failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") + javaClassFile("Parent.class").assertDoesNotExist() + } else { + classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 + } + + where: + sourceCompatibility | toolchain | fails + JavaVersion.VERSION_17 | JavaVersion.VERSION_17 | false + JavaVersion.VERSION_11 | JavaVersion.VERSION_17 | true + } + + def "release flag lower than compiler version does not allow accessing newer Java language features"() { + def jdk = AvailableJavaHomes.getJdk(toolchain) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.options.release = ${release.majorVersion} + """ + + file("src/main/java/Parent.java") << """ + // 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 {} + } + """ + + when: + if (fails) { + withInstallations(jdk).fails(":compileJava") + } else { + withInstallations(jdk).succeeds(":compileJava") + } + + then: + if (fails) { + failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") + javaClassFile("Parent.class").assertDoesNotExist() + } else { + classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 + } + + where: + release | toolchain | fails + JavaVersion.VERSION_17 | JavaVersion.VERSION_17 | false + JavaVersion.VERSION_11 | JavaVersion.VERSION_17 | true + } + + 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") << """ + 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); + } + } + """ + + // 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(toolchain) + + buildFile << """ + apply plugin: "java" + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}) + } + } + + compileJava.options.release = ${release.majorVersion} + """ + + file("src/main/java/Main.java") << """ + 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); + } + } + """ + + when: + if (fails) { + withInstallations(jdk).fails(":compileJava") + } else { + withInstallations(jdk).succeeds(":compileJava") + } + + then: + if (fails) { + failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") + failure.assertHasErrorOutput("symbol: method absExact(int)") + javaClassFile("Main.class").assertDoesNotExist() + } else { + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 + } + + where: + release | toolchain | fails + JavaVersion.VERSION_17 | JavaVersion.VERSION_17 | false + JavaVersion.VERSION_11 | JavaVersion.VERSION_17 | true + } + + def "lower toolchain does not allow accessing newer JDK APIs"() { + def jdk = AvailableJavaHomes.getJdk(toolchain) + + 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") << """ + 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); + } + } + """ + + when: + if (fails) { + withInstallations(jdk).fails(":compileJava") + } else { + withInstallations(jdk).succeeds(":compileJava") + } + + then: + // Configuring a toolchain only affects sourceCompatibility and not release + outputContains("Source is set to '${toolchain}'") + outputContains("Release is set to 'null'") + + // But compilation still fails, because the compiler is effectively older and does not support the newer APIs + if (fails) { + failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") + failure.assertHasErrorOutput("symbol: method absExact(int)") + javaClassFile("Main.class").assertDoesNotExist() + } else { + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 + } + + where: + toolchain | fails + JavaVersion.VERSION_17 | false + JavaVersion.VERSION_11 | true + } + private static String currentJavaVersion() { return Jvm.current().javaVersion.toString() } From 192074d059a19c6658334d43127229c9efab6a23 Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Fri, 9 Dec 2022 16:58:01 +0100 Subject: [PATCH 067/120] Fix support of -PtestVersions= for cross version tests --- .../gradlebuild.cross-version-tests.gradle.kts | 3 ++- .../gradlebuild.distribution-testing.gradle.kts | 11 +---------- .../integrationtests/shared-configuration.kt | 12 ++++++++++++ .../fixtures/CrossVersionIntegrationSpec.groovy | 3 +++ 4 files changed, 18 insertions(+), 11 deletions(-) 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..e17cb676468d 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 @@ -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..fd0804de6f78 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 = "default") { + // 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/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 { From 7f6ae8386cd70dc893c9c5e735b9a4fa81dbd584 Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Fri, 9 Dec 2022 16:59:44 +0100 Subject: [PATCH 068/120] Fix metadata cache layout version bump The fix only matters for cross version test as already released version look at DefaultArtifactCacheMetadata.CACHE_LAYOUT_VERSION for determining their own version. --- .../gradle/api/internal/artifacts/ivyservice/CacheLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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")), From 7dc878b047020c8b59743c00922c7a0713538c3c Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Mon, 12 Dec 2022 09:56:14 +0100 Subject: [PATCH 069/120] Remove default parameter value Only two call sites, let's have them explicit. --- .../src/main/kotlin/gradlebuild.distribution-testing.gradle.kts | 2 +- .../kotlin/gradlebuild/integrationtests/shared-configuration.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 e17cb676468d..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 @@ -39,7 +39,7 @@ tasks.withType().configureEach { shouldRunAfter("test") setJvmArgsOfTestJvm() - setSystemPropertiesOfTestJVM() + setSystemPropertiesOfTestJVM("default") configureGradleTestEnvironment() addSetUpAndTearDownActions() } 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 fd0804de6f78..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 @@ -177,7 +177,7 @@ fun IntegrationTest.addDebugProperties() { } -fun DistributionTest.setSystemPropertiesOfTestJVM(defaultVersions: String = "default") { +fun DistributionTest.setSystemPropertiesOfTestJVM(defaultVersions: String) { // use -PtestVersions=all or -PtestVersions=1.2,1.3… val integTestVersionsSysProp = "org.gradle.integtest.versions" if (project.hasProperty("testVersions")) { From 6ebc32552032c7415cbbe2ed81a23ad45d00846e Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Mon, 5 Dec 2022 18:16:23 +0100 Subject: [PATCH 070/120] Improve readme --- subprojects/docs/README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) 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 From c8bdda9ecf7621375325981f3c4aa160fd73df13 Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Mon, 5 Dec 2022 18:18:29 +0100 Subject: [PATCH 071/120] Update ForkOptions javadoc --- .../gradle/api/tasks/compile/ForkOptions.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 */ From 6f8a6ffe4fc1079770259807ef1a790ce9763c73 Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Mon, 12 Dec 2022 12:28:37 +0100 Subject: [PATCH 072/120] Fix JavaCompile tests for configuration cache --- ...CompileCompatibilityIntegrationTest.groovy | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) 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 index 787f72db9c11..20fba4f03550 100644 --- 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 @@ -211,11 +211,15 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im ${target != 'none' ? "targetCompatibility = JavaVersion.toVersion($target)" : ''} } - compileJava.doLast { - logger.lifecycle("project.sourceCompatibility = '\${project.java.sourceCompatibility}'") - logger.lifecycle("project.targetCompatibility = '\${project.java.targetCompatibility}'") - logger.lifecycle("task.sourceCompatibility = '\$sourceCompatibility'") - logger.lifecycle("task.targetCompatibility = '\$targetCompatibility'") + 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'") + } } """ @@ -250,11 +254,15 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } } - compileJava.doLast { - logger.lifecycle("project.sourceCompatibility = '\${project.java.sourceCompatibility}'") - logger.lifecycle("project.targetCompatibility = '\${project.java.targetCompatibility}'") - logger.lifecycle("task.sourceCompatibility = '\$sourceCompatibility'") - logger.lifecycle("task.targetCompatibility = '\$targetCompatibility'") + 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'") + } } """ @@ -286,11 +294,15 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im targetCompatibility = null } - compileJava.doLast { - logger.lifecycle("project.sourceCompatibility = '\${project.java.sourceCompatibility}'") - logger.lifecycle("project.targetCompatibility = '\${project.java.targetCompatibility}'") - logger.lifecycle("task.sourceCompatibility = '\$sourceCompatibility'") - logger.lifecycle("task.targetCompatibility = '\$targetCompatibility'") + 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'") + } } """ From e70c20d3ecca04def5aee3fd4bf2a06aa5dd2a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Arias=20Segu=C3=AD?= Date: Mon, 12 Dec 2022 12:41:48 +0100 Subject: [PATCH 073/120] Remove suppress deprecation warnings Also cleanup some redundant qualified names, and add type generics to remove warnings. --- .../AnnotationProcessingTasks.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) 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; } From 9e11a8d0ac8b36c504cb7a41ab0f82a9d95c8c13 Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Mon, 12 Dec 2022 14:14:27 +0100 Subject: [PATCH 074/120] Make testVersions=partial equivalent to latest This helps resolve a corner case in our build setup where we get a partial for tests that only support latest at this point. It can be argued that latest is a form of partial coverage. --- .../integtests/tooling/fixture/CrossVersionTestEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); } From 573b3f9697ebc3ea418dc6ae86d253f8f1bc2073 Mon Sep 17 00:00:00 2001 From: Matthias Ernst Date: Fri, 18 Nov 2022 09:14:05 +0100 Subject: [PATCH 075/120] Remove ExternalBinariesLookup implementation Fixes https://github.com/gradle/gradle/issues/20101: `Scala Incremental Compilation for Multi-Module projects broken in 7.x` Since Gradle 7.x, Gradle's implementation of `ExternalLookup` has become out of date with Zinc's file stamping and always triggers full recompilation. Since an implementation of `ExternalLookup` is optional and [Zinc's default](https://github.com/sbt/zinc/blob/develop/internal/zinc-core/src/main/scala/sbt/internal/inc/IncrementalCommon.scala#L734) already does what Gradle is trying to do here, the Gradle-customization can be completely removed. Signed-off-by: matthias_ernst --- .../tasks/scala/ZincScalaCompiler.java | 88 ------------------- 1 file changed, 88 deletions(-) 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; From a1664ccb5c51e00a2cdd4f73b0f064eca7ceb741 Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Fri, 9 Dec 2022 15:25:10 +0100 Subject: [PATCH 076/120] Test Scala 3 incremental compilation Signed-off-by: Alex Semin --- .../scala/compile/ZincScalaCompilerIntegrationTest.groovy | 8 -------- 1 file changed, 8 deletions(-) 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 = """ From f6b1eb3b58f08ce9d505b24c0bfb3bef317aaccf Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Sat, 10 Dec 2022 18:39:52 +0100 Subject: [PATCH 077/120] Error on matching attributes and capabilities Having multiple consumable configurations with the same attributes and capability is no longer supported. However, this is handled in a lenient fashion for the outgoing variants report to allow users to visualize the issue. --- .../ExclusiveVariantsIntegrationTest.groovy | 64 +++++++++++++++---- .../configurations/ConfigurationInternal.java | 10 +++ .../configurations/DefaultConfiguration.java | 60 ++++++++++++----- .../ConfigurationReportModelFactory.java | 5 +- .../model/ReportConfiguration.java | 9 +++ .../ConsoleConfigurationReportRenderer.java | 10 +++ .../04-modeling-features/variant_model.adoc | 23 +++++-- .../migration/upgrading_version_7.adoc | 10 ++- 8 files changed, 153 insertions(+), 38 deletions(-) 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..986d00c03429 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() + + consumer { + canBeResolved = true + canBeConsumed = false + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "custom")) + } + } + } + + dependencies { + consumer(project) + } + + tasks.register('resolveSample', Copy) { + from configurations.consumer + 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/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 e08da3fbe1c9..41863791e5bc 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; @@ -88,8 +89,17 @@ enum InternalState { boolean isCanBeMutated(); + /** + * Lock the configuration, with the option to be lenient on some failures + *

      + * The idea behind the leniency is to allow report tasks to still print information + */ void preventFromFurtherMutation(); + void preventFromFurtherMutation(boolean lenient); + + List getLenientErrors(); + /** * 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..4f75d82879c2 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; @@ -138,6 +141,7 @@ import javax.annotation.Nullable; import java.io.File; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -147,8 +151,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; @@ -249,6 +253,7 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf private String consistentResolutionReason; private ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory; private final DefaultConfigurationFactory defaultConfigurationFactory; + private List lenientErrors; /** * To create an instance, use {@link DefaultConfigurationFactory#create}. @@ -1101,6 +1106,19 @@ public boolean isCanBeMutated() { @Override public void preventFromFurtherMutation() { + preventFromFurtherMutation(false); + } + + @Override + public List getLenientErrors() { + if (lenientErrors == null) { + return Collections.emptyList(); + } + return lenientErrors; + } + + @Override + public void preventFromFurtherMutation(boolean lenient) { // TODO This should use the same `MutationValidator` infrastructure that we use for other mutation types if (canBeMutated) { if (beforeLocking != null) { @@ -1116,31 +1134,43 @@ 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(); + ensureUniqueAttributes(lenient); } } } - private void ensureUniqueAttributes() { + private void 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) { + if (lenientErrors == null) { + lenientErrors = new ArrayList<>(); + } + lenientErrors.add(gradleException); + } else { + throw gradleException; + } + } } } 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..b88f7db5a52a 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 @@ -78,7 +78,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(); + configuration.preventFromFurtherMutation(true); final List attributes = configuration.getAttributes().keySet().stream() .map(a -> convertAttributeInContainer(a, configuration.getAttributes(), project.getDependencies().getAttributesSchema())) @@ -117,7 +117,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<>(configuration.getLenientErrors()), + 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..8804b28622f3 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 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 = 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/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/migration/upgrading_version_7.adoc b/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc index 1d6f8c8fa0c3..d3a98ddfb99e 100644 --- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc +++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc @@ -517,6 +517,13 @@ 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. + [[changes_7.6]] == Upgrading from 7.5 and earlier @@ -1068,7 +1075,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. From 020505daceb778fb8ba20d88fb8553b09e4317c3 Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Sun, 11 Dec 2022 12:46:06 +0100 Subject: [PATCH 078/120] Remove added field to DefaultConfiguration There can be a lot of configuration instances in a given build, so a solution without an added field is better for memory consumption. --- .../configurations/ConfigurationInternal.java | 4 +--- .../configurations/DefaultConfiguration.java | 23 +++++++------------ .../ConfigurationReportModelFactory.java | 5 ++-- 3 files changed, 12 insertions(+), 20 deletions(-) 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 41863791e5bc..809ee74d9a62 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 @@ -96,9 +96,7 @@ enum InternalState { */ void preventFromFurtherMutation(); - void preventFromFurtherMutation(boolean lenient); - - List getLenientErrors(); + List preventFromFurtherMutation(boolean lenient); /** * Reports whether this configuration uses {@link org.gradle.api.Incubating Incubating} attributes types, such as {@link org.gradle.api.attributes.Category#VERIFICATION}. 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 4f75d82879c2..9866e11dc7e4 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 @@ -253,7 +253,6 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf private String consistentResolutionReason; private ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory; private final DefaultConfigurationFactory defaultConfigurationFactory; - private List lenientErrors; /** * To create an instance, use {@link DefaultConfigurationFactory#create}. @@ -1110,17 +1109,11 @@ public void preventFromFurtherMutation() { } @Override - public List getLenientErrors() { - if (lenientErrors == null) { - return Collections.emptyList(); - } - return lenientErrors; - } - - @Override - public void preventFromFurtherMutation(boolean lenient) { + public List preventFromFurtherMutation(boolean lenient) { // TODO This should use the same `MutationValidator` infrastructure that we use for other mutation types if (canBeMutated) { + List lenientErrors = new ArrayList<>(); + if (beforeLocking != null) { beforeLocking.execute(this); beforeLocking = null; @@ -1134,12 +1127,15 @@ public void preventFromFurtherMutation(boolean lenient) { // We will only check unique attributes if this configuration is consumable, not resolvable, and has attributes itself if (mustHaveUniqueAttributes(this) && !this.getAttributes().isEmpty()) { - ensureUniqueAttributes(lenient); + ensureUniqueAttributes(lenient, lenientErrors); } + + return lenientErrors; } + return Collections.emptyList(); } - private void ensureUniqueAttributes(boolean lenient) { + private void ensureUniqueAttributes(boolean lenient, List lenientErrors) { final Set all = (configurationsProvider != null) ? configurationsProvider.getAll() : null; if (all != null) { final Collection allCapabilities = allCapabilitiesIncludingDefault(this); @@ -1163,9 +1159,6 @@ private void ensureUniqueAttributes(boolean lenient) { .withUserManual("upgrading_version_7", "unique_attribute_sets") .build(); if (lenient) { - if (lenientErrors == null) { - lenientErrors = new ArrayList<>(); - } lenientErrors.add(gradleException); } else { throw gradleException; 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 b88f7db5a52a..9985a9c7f2fd 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(true); + List lenientErrors = configuration.preventFromFurtherMutation(true); final List attributes = configuration.getAttributes().keySet().stream() .map(a -> convertAttributeInContainer(a, configuration.getAttributes(), project.getDependencies().getAttributesSchema())) @@ -117,7 +118,7 @@ private ReportConfiguration convertConfiguration(ConfigurationInternal configura type = null; } - return new ReportConfiguration(configuration.getName(), configuration.getDescription(), type, new ArrayList<>(configuration.getLenientErrors()), + return new ReportConfiguration(configuration.getName(), configuration.getDescription(), type, new ArrayList<>(lenientErrors), attributes, capabilities, artifacts, variants, extendedConfigurations); } From 9acf946d82747270c0f04561f4fa57a1293aea15 Mon Sep 17 00:00:00 2001 From: Alex Semin Date: Mon, 12 Dec 2022 17:20:21 +0100 Subject: [PATCH 079/120] Polish JavaCompile compatibility tests --- ...CompileCompatibilityIntegrationTest.groovy | 310 ++++++++---------- 1 file changed, 142 insertions(+), 168 deletions(-) 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 index 20fba4f03550..b6ff230bb92b 100644 --- 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 @@ -141,7 +141,7 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im compileJava { options.fork = true - ${configure.replace("", path)} + ${configure(path)} } compileJava.doLast { @@ -161,37 +161,9 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im classJavaVersion(javaClassFile("Foo.class")) == earlierJdk.javaVersion where: - forkOption | configure | appendPath - "java home" | 'options.forkOptions.javaHome = file("")' | '' - "executable" | 'options.forkOptions.executable = ""' | OperatingSystem.current().getExecutableName('/bin/javac') - } - - 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}'.") - classJavaVersion(javaClassFile("Foo.class")) == jdk.javaVersion + 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)"() { @@ -277,48 +249,43 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im classJavaVersion(javaClassFile("Foo.class")) == JavaVersion.toVersion(prevJavaVersion) } - def "configuring toolchain on java extension and clearing source and target compatibility is supported"() { - def jdk = Jvm.current() - def javaVersion = jdk.javaVersion + 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' + apply plugin: "java" java { - sourceCompatibility = JavaVersion.VERSION_14 - targetCompatibility = JavaVersion.VERSION_14 toolchain { - languageVersion = JavaLanguageVersion.of(${javaVersion.majorVersion}) + languageVersion = JavaLanguageVersion.of(${jdk.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'") - } - } + // Lower than Java 17 + compileJava.sourceCompatibility = "${JavaVersion.VERSION_11}" """ + file("src/main/java/Parent.java") << sourceUsingLanguageFeatureFromJava17() + when: - withInstallations(jdk).run(":compileJava") + withInstallations(jdk).fails(":compileJava") then: - outputContains("project.sourceCompatibility = '$javaVersion'") - outputContains("project.targetCompatibility = '$javaVersion'") - outputContains("task.sourceCompatibility = '$javaVersion'") - outputContains("task.targetCompatibility = '$javaVersion'") - classJavaVersion(javaClassFile("Foo.class")) == javaVersion + failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") + javaClassFile("Parent.class").assertDoesNotExist() } - def "source compatibility lower than compiler version does not allow accessing newer Java language features"() { - def jdk = AvailableJavaHomes.getJdk(toolchain) + def "source compatibility matching the compiler version allows accessing Java language features"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) buildFile << """ apply plugin: "java" @@ -329,39 +296,21 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } } - compileJava.sourceCompatibility = "$sourceCompatibility" + compileJava.sourceCompatibility = "${JavaVersion.VERSION_17}" """ - file("src/main/java/Parent.java") << """ - // 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 {} - } - """ + file("src/main/java/Parent.java") << sourceUsingLanguageFeatureFromJava17() when: - if (fails) { - withInstallations(jdk).fails(":compileJava") - } else { - withInstallations(jdk).succeeds(":compileJava") - } + withInstallations(jdk).succeeds(":compileJava") then: - if (fails) { - failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") - javaClassFile("Parent.class").assertDoesNotExist() - } else { - classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 - } - - where: - sourceCompatibility | toolchain | fails - JavaVersion.VERSION_17 | JavaVersion.VERSION_17 | false - JavaVersion.VERSION_11 | JavaVersion.VERSION_17 | true + 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(toolchain) + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) buildFile << """ apply plugin: "java" @@ -372,35 +321,57 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } } - compileJava.options.release = ${release.majorVersion} + // Lower than Java 17 + compileJava.options.release = ${JavaVersion.VERSION_11.majorVersion} """ - file("src/main/java/Parent.java") << """ - // 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 {} + 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: - if (fails) { - withInstallations(jdk).fails(":compileJava") - } else { - withInstallations(jdk).succeeds(":compileJava") - } + withInstallations(jdk).succeeds(":compileJava") then: - if (fails) { - failure.assertHasErrorOutput("Parent.java:3: error: sealed classes are not supported in -source 11") - javaClassFile("Parent.class").assertDoesNotExist() - } else { - classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 - } + executedAndNotSkipped(":compileJava") + classJavaVersion(javaClassFile("Parent.class")) == JavaVersion.VERSION_17 + } - where: - release | toolchain | fails - JavaVersion.VERSION_17 | JavaVersion.VERSION_17 | false - JavaVersion.VERSION_11 | JavaVersion.VERSION_17 | true + 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"() { @@ -432,17 +403,7 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } """ - file("src/main/java/Main.java") << """ - 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); - } - } - """ + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() // Compiling with Java 11 fails, because the source code uses a Java 15 API when: @@ -476,7 +437,7 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } def "release flag lower than compiler version does not allow accessing newer JDK APIs"() { - def jdk = AvailableJavaHomes.getJdk(toolchain) + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_17) buildFile << """ apply plugin: "java" @@ -487,45 +448,48 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } } - compileJava.options.release = ${release.majorVersion} + // Lower than Java 15 + compileJava.options.release = ${JavaVersion.VERSION_11.majorVersion} """ - file("src/main/java/Main.java") << """ - public class Main { - public static void main(String[] args) { - System.out.println("Main: java " + System.getProperty("java.version")); + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() - // API added in Java 15 - long x = Math.absExact(-42); - System.out.println("Main: value " + x); + 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: - if (fails) { - withInstallations(jdk).fails(":compileJava") - } else { - withInstallations(jdk).succeeds(":compileJava") - } + withInstallations(jdk).succeeds(":compileJava") then: - if (fails) { - failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") - failure.assertHasErrorOutput("symbol: method absExact(int)") - javaClassFile("Main.class").assertDoesNotExist() - } else { - classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 - } - - where: - release | toolchain | fails - JavaVersion.VERSION_17 | JavaVersion.VERSION_17 | false - JavaVersion.VERSION_11 | JavaVersion.VERSION_17 | true + executedAndNotSkipped(":compileJava") + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 } - def "lower toolchain does not allow accessing newer JDK APIs"() { - def jdk = AvailableJavaHomes.getJdk(toolchain) + def "earlier toolchain does not allow accessing later JDK APIs in source"() { + def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) buildFile << """ apply plugin: "java" @@ -542,43 +506,53 @@ class JavaCompileCompatibilityIntegrationTest extends AbstractIntegrationSpec im } """ - file("src/main/java/Main.java") << """ - public class Main { - public static void main(String[] args) { - System.out.println("Main: java " + System.getProperty("java.version")); + file("src/main/java/Main.java") << sourceUsingJavaApiFromJava15() - // API added in Java 15 - long x = Math.absExact(-42); - System.out.println("Main: value " + x); + 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: - if (fails) { - withInstallations(jdk).fails(":compileJava") - } else { - withInstallations(jdk).succeeds(":compileJava") - } + withInstallations(jdk).succeeds(":compileJava") then: + executedAndNotSkipped(":compileJava") + // Configuring a toolchain only affects sourceCompatibility and not release - outputContains("Source is set to '${toolchain}'") + outputContains("Source is set to '${JavaVersion.VERSION_17}'") outputContains("Release is set to 'null'") - // But compilation still fails, because the compiler is effectively older and does not support the newer APIs - if (fails) { - failure.assertHasErrorOutput("Main.java:7: error: cannot find symbol") - failure.assertHasErrorOutput("symbol: method absExact(int)") - javaClassFile("Main.class").assertDoesNotExist() - } else { - classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 - } - - where: - toolchain | fails - JavaVersion.VERSION_17 | false - JavaVersion.VERSION_11 | true + classJavaVersion(javaClassFile("Main.class")) == JavaVersion.VERSION_17 } private static String currentJavaVersion() { From dc000cd66a6d517e9023449edc0a7ff4b5908a3f Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 12 Dec 2022 11:25:23 -0500 Subject: [PATCH 080/120] Fix temporary cache directory creation and usage in tests --- .../file/archive/AbstractArchiveFileTreeSpec.groovy | 11 ++++++----- .../impl/DefaultFileCollectionSnapshotterTest.groovy | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy index b777640532d3..a1c9018d3bb0 100644 --- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy +++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/archive/AbstractArchiveFileTreeSpec.groovy @@ -18,15 +18,16 @@ package org.gradle.api.internal.file.archive import org.gradle.api.file.FileVisitor import org.gradle.api.internal.file.FileTreeInternal -import org.gradle.api.internal.file.TestFiles 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.scopes.ScopedCache +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 + /** * Tests core functionality in {@link AbstractArchiveFileTree} using a minimal test implementation. */ @@ -39,7 +40,7 @@ class AbstractArchiveFileTreeSpec extends Specification { def visitor = Mock(MinimalFileTree.MinimalFileTreeStructureVisitor) def backingFile = tmpDir.createFile("thing.bin") - def fileTree = new TestArchiveFileTree(TestFiles.scopedCache(tmpDir.createDir("cache-dir")), backingFile) + def fileTree = new TestArchiveFileTree(TestCaches.decompressionCache(tmpDir.createDir("cache-dir")), backingFile) when: fileTree.visitStructure(visitor, owner) @@ -53,8 +54,8 @@ class AbstractArchiveFileTreeSpec extends Specification { File backingFile final String displayName = "" - TestArchiveFileTree(ScopedCache decompressionCacheFactory, File backingFile) { - super(decompressionCacheFactory) + TestArchiveFileTree(DecompressionCache decompressionCache, File backingFile) { + super(decompressionCache) this.backingFile = backingFile } 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 From c8e658df2d2de5f9fe3906e5710217bdd91a63d2 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 12 Dec 2022 11:58:50 -0500 Subject: [PATCH 081/120] Adjusts the BuildTreeScoped decompression cache factory to automatically close it's created caches when it is closed Also refactors this and the ProjectScoped cache factory to share a common abstract base class which handles managing the closings. Makes the ProjectScoped factory thread-safe. --- ...tractManagedDecompressionCacheFactory.java | 62 +++++++++++++++++++ ...ldTreeScopedDecompressionCacheFactory.java | 36 +++++++++++ ...rojectScopedDecompressionCacheFactory.java | 60 +++++++----------- .../buildtree/BuildTreeScopeServices.java | 14 +---- 4 files changed, 123 insertions(+), 49 deletions(-) create mode 100644 subprojects/core/src/main/java/org/gradle/api/internal/cache/AbstractManagedDecompressionCacheFactory.java create mode 100644 subprojects/core/src/main/java/org/gradle/api/internal/cache/BuildTreeScopedDecompressionCacheFactory.java diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/AbstractManagedDecompressionCacheFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/AbstractManagedDecompressionCacheFactory.java new file mode 100644 index 000000000000..94b199e3f750 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/AbstractManagedDecompressionCacheFactory.java @@ -0,0 +1,62 @@ +/* + * 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 org.gradle.internal.concurrent.Stoppable; + +import java.io.Closeable; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Abstract base class for implementing factories that can be used to create {@link DecompressionCache}s. + * + * This class manages the caches it creates by keeping track of them, and closing them when this factory is + * itself closed or stopped. This allows the caches to be closed when the owning scope containing the + * service instance of the factory implementation is closed. + */ +/* package */ abstract class AbstractManagedDecompressionCacheFactory implements DecompressionCacheFactory, Stoppable, Closeable { + private final Set createdCaches = new HashSet<>(); + + protected abstract ScopedCache getScopedCache(); + + @Override + public DefaultDecompressionCache create() { + DefaultDecompressionCache cache = new DefaultDecompressionCache(getScopedCache()); + createdCaches.add(cache); + return cache; + } + + @Override + public void close() throws IOException { + createdCaches.forEach(DefaultDecompressionCache::close); + } + + @Override + public void stop() { + try { + close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/BuildTreeScopedDecompressionCacheFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/BuildTreeScopedDecompressionCacheFactory.java new file mode 100644 index 000000000000..4fbe888eeea5 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/BuildTreeScopedDecompressionCacheFactory.java @@ -0,0 +1,36 @@ +/* + * 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.scopes.BuildTreeScopedCache; + +/** + * An implementation of {@link AbstractManagedDecompressionCacheFactory} that creates + * decompression caches. + */ +public final class BuildTreeScopedDecompressionCacheFactory extends AbstractManagedDecompressionCacheFactory { + private final BuildTreeScopedCache cacheFactory; + + public BuildTreeScopedDecompressionCacheFactory(BuildTreeScopedCache cacheFactory) { + this.cacheFactory = cacheFactory; + } + + @Override + protected BuildTreeScopedCache getScopedCache() { + return cacheFactory; + } +} diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java index 99047c8dcf94..c79f12577582 100644 --- a/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java +++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/ProjectScopedDecompressionCacheFactory.java @@ -19,34 +19,31 @@ import org.gradle.api.file.ProjectLayout; import org.gradle.cache.CacheRepository; import org.gradle.cache.internal.CacheFactory; -import org.gradle.cache.internal.DecompressionCache; -import org.gradle.cache.internal.DecompressionCacheFactory; import org.gradle.cache.internal.DefaultCacheRepository; -import org.gradle.cache.internal.DefaultDecompressionCache; import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping; import org.gradle.cache.internal.scopes.DefaultProjectScopedCache; import org.gradle.cache.scopes.ProjectScopedCache; -import org.gradle.internal.concurrent.Stoppable; import org.gradle.util.GradleVersion; -import java.io.Closeable; +import javax.annotation.concurrent.GuardedBy; import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; /** - * A factory that can be used to create {@link DecompressionCache} instances for a particular project. + * A lazy implementation of {@link AbstractManagedDecompressionCacheFactory} that creates + * decompression caches for a project. * - * This type exists so that creation of the caches can be done lazily, avoiding the - * creation of the cache directory (and, by default, the build directory it will be - * a subdirectory of) until it is actually needed. We need a named factory type + * This implementation provides for lazy creation of the caches, avoiding the + * need to read the cache directory (and, by default, the build directory it will be + * a subdirectory of) until we are actually creating a cache. We need a named factory type * to create a service which can be injected. */ -public class ProjectScopedDecompressionCacheFactory implements DecompressionCacheFactory, Stoppable, Closeable { +public final class ProjectScopedDecompressionCacheFactory extends AbstractManagedDecompressionCacheFactory { private final ProjectLayout projectLayout; private final CacheFactory cacheFactory; - private final Set createdCaches = new HashSet<>(); + + private final Object[] lock = new Object[0]; + @GuardedBy("lock") + private volatile ProjectScopedCache scopedCache; public ProjectScopedDecompressionCacheFactory(ProjectLayout projectLayout, CacheFactory cacheFactory) { this.projectLayout = projectLayout; @@ -54,31 +51,20 @@ public ProjectScopedDecompressionCacheFactory(ProjectLayout projectLayout, Cache } @Override - public DefaultDecompressionCache create() { - File cacheDir = projectLevelCacheDir(); - CacheRepository cacheRepository = new DefaultCacheRepository(new DefaultCacheScopeMapping(cacheDir, GradleVersion.current()), cacheFactory); - ProjectScopedCache scopedCache = new DefaultProjectScopedCache(cacheDir, cacheRepository); - - DefaultDecompressionCache cache = new DefaultDecompressionCache(scopedCache); - createdCaches.add(cache); - return cache; + protected ProjectScopedCache getScopedCache() { + if (scopedCache == null) { + synchronized (lock) { + if (scopedCache == null) { + File cacheDir = getCacheDir(); + CacheRepository cacheRepository = new DefaultCacheRepository(new DefaultCacheScopeMapping(cacheDir, GradleVersion.current()), cacheFactory); + scopedCache = new DefaultProjectScopedCache(cacheDir, cacheRepository); + } + } + } + return scopedCache; } - private File projectLevelCacheDir() { + private File getCacheDir() { return projectLayout.getBuildDirectory().file(".cache").get().getAsFile(); } - - @Override - public void close() throws IOException { - createdCaches.forEach(DefaultDecompressionCache::close); - } - - @Override - public void stop() { - try { - close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } } 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 ee46af8563b5..9c7395feeb69 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,13 +17,12 @@ package org.gradle.internal.buildtree; import org.gradle.StartParameter; +import org.gradle.api.internal.cache.BuildTreeScopedDecompressionCacheFactory; 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.DecompressionCache; import org.gradle.cache.internal.DecompressionCacheFactory; -import org.gradle.cache.internal.DefaultDecompressionCache; import org.gradle.cache.scopes.BuildTreeScopedCache; import org.gradle.execution.DefaultTaskSelector; import org.gradle.execution.ProjectConfigurer; @@ -100,16 +99,7 @@ protected ExceptionAnalyser createExceptionAnalyser(LoggingConfiguration logging return exceptionAnalyser; } - protected DecompressionCache createDecompressionCache(BuildTreeScopedCache cacheFactory) { - return new DefaultDecompressionCache(cacheFactory); - } - protected DecompressionCacheFactory createDecompressionCacheFactory(BuildTreeScopedCache cacheFactory) { - return new DecompressionCacheFactory() { - @Override - public DecompressionCache create() { - return new DefaultDecompressionCache(cacheFactory); - } - }; + return new BuildTreeScopedDecompressionCacheFactory(cacheFactory); } } From b450863fb153c604c47962c76e124741873f1cd0 Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Mon, 12 Dec 2022 18:07:59 +0100 Subject: [PATCH 082/120] Refactor lenient mutation locking Cleaner method separation, removed spurious parameter. --- .../configurations/ConfigurationInternal.java | 2 +- .../configurations/DefaultConfiguration.java | 17 +++++++++-------- .../model/ConfigurationReportModelFactory.java | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) 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 809ee74d9a62..4d3667abef80 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 @@ -96,7 +96,7 @@ enum InternalState { */ void preventFromFurtherMutation(); - List preventFromFurtherMutation(boolean lenient); + List preventFromFurtherMutationLenient(); /** * Reports whether this configuration uses {@link org.gradle.api.Incubating Incubating} attributes types, such as {@link org.gradle.api.attributes.Category#VERIFICATION}. 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 9866e11dc7e4..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 @@ -141,7 +141,6 @@ import javax.annotation.Nullable; import java.io.File; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -1109,11 +1108,13 @@ public void preventFromFurtherMutation() { } @Override - public List preventFromFurtherMutation(boolean lenient) { + 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) { - List lenientErrors = new ArrayList<>(); - if (beforeLocking != null) { beforeLocking.execute(this); beforeLocking = null; @@ -1127,15 +1128,14 @@ public List preventFromFurtherMutation(boolean lenien // We will only check unique attributes if this configuration is consumable, not resolvable, and has attributes itself if (mustHaveUniqueAttributes(this) && !this.getAttributes().isEmpty()) { - ensureUniqueAttributes(lenient, lenientErrors); + return ensureUniqueAttributes(lenient); } - return lenientErrors; } return Collections.emptyList(); } - private void ensureUniqueAttributes(boolean lenient, List lenientErrors) { + private List ensureUniqueAttributes(boolean lenient) { final Set all = (configurationsProvider != null) ? configurationsProvider.getAll() : null; if (all != null) { final Collection allCapabilities = allCapabilitiesIncludingDefault(this); @@ -1159,12 +1159,13 @@ private void ensureUniqueAttributes(boolean lenient, List lenie .withUserManual("upgrading_version_7", "unique_attribute_sets") .build(); if (lenient) { - lenientErrors.add(gradleException); + return Collections.singletonList(gradleException); } else { throw gradleException; } } } + return Collections.emptyList(); } /** 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 9985a9c7f2fd..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 @@ -79,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 - List lenientErrors = configuration.preventFromFurtherMutation(true); + List lenientErrors = configuration.preventFromFurtherMutationLenient(); final List attributes = configuration.getAttributes().keySet().stream() .map(a -> convertAttributeInContainer(a, configuration.getAttributes(), project.getDependencies().getAttributesSchema())) From 7af24f5a45ad28a2c59af63d1ee60a763d57aa83 Mon Sep 17 00:00:00 2001 From: Louis Jacomet Date: Mon, 12 Dec 2022 18:16:59 +0100 Subject: [PATCH 083/120] Make ReportConfiguration immutable The newly introduced field was breaking the immutability of the class. --- .../internal/configurations/model/ReportConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8804b28622f3..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 @@ -32,7 +32,7 @@ public final class ReportConfiguration { @Nullable private final Type type; - private List lenientErrors; + private final List lenientErrors; private final ImmutableList attributes; private final ImmutableList capabilities; private final ImmutableList artifacts; @@ -49,7 +49,7 @@ public final class ReportConfiguration { this.name = name; this.description = description; this.type = type; - this.lenientErrors = lenientErrors; + this.lenientErrors = ImmutableList.copyOf(lenientErrors); this.attributes = ImmutableList.copyOf(attributes); this.capabilities = ImmutableList.copyOf(capabilities); this.artifacts = ImmutableList.copyOf(artifacts); From e0d9e4157d78754dc340b725786839a00ffc2940 Mon Sep 17 00:00:00 2001 From: Gary Hale Date: Tue, 6 Dec 2022 15:03:17 -0500 Subject: [PATCH 084/120] Add documentation for cache cleanup configuration --- ...org.gradle.api.initialization.Settings.xml | 6 +++ subprojects/docs/src/docs/release/notes.md | 25 +++++++++++ .../userguide/reference/directory_layout.adoc | 43 ++++++++++++++++--- .../cacheRetention/groovy/build.gradle | 0 .../init.d/cache-settings.gradle | 8 ++++ .../cacheRetention/kotlin/build.gradle.kts | 0 .../init.d/cache-settings.gradle.kts | 8 ++++ .../tests-groovy/cacheRetention.sample.conf | 3 ++ .../tests-kotlin/cacheRetention.sample.conf | 3 ++ .../disableCacheCleanup/groovy/build.gradle | 0 .../init.d/cache-settings.gradle | 5 +++ .../kotlin/build.gradle.kts | 0 .../init.d/cache-settings.gradle.kts | 5 +++ .../tests-groovy/cacheRetention.sample.conf | 3 ++ .../tests-kotlin/cacheRetention.sample.conf | 3 ++ 15 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/build.gradle create mode 100644 subprojects/docs/src/snippets/initScripts/cacheRetention/groovy/gradleUserHome/init.d/cache-settings.gradle create mode 100644 subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/build.gradle.kts create mode 100644 subprojects/docs/src/snippets/initScripts/cacheRetention/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts create mode 100644 subprojects/docs/src/snippets/initScripts/cacheRetention/tests-groovy/cacheRetention.sample.conf create mode 100644 subprojects/docs/src/snippets/initScripts/cacheRetention/tests-kotlin/cacheRetention.sample.conf create mode 100644 subprojects/docs/src/snippets/initScripts/disableCacheCleanup/groovy/build.gradle create mode 100644 subprojects/docs/src/snippets/initScripts/disableCacheCleanup/groovy/gradleUserHome/init.d/cache-settings.gradle create mode 100644 subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/build.gradle.kts create mode 100644 subprojects/docs/src/snippets/initScripts/disableCacheCleanup/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts create mode 100644 subprojects/docs/src/snippets/initScripts/disableCacheCleanup/tests-groovy/cacheRetention.sample.conf create mode 100644 subprojects/docs/src/snippets/initScripts/disableCacheCleanup/tests-kotlin/cacheRetention.sample.conf 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..152b44c58459 100644 --- a/subprojects/docs/src/docs/release/notes.md +++ b/subprojects/docs/src/docs/release/notes.md @@ -257,6 +257,31 @@ ADD RELEASE FEATURES ABOVE --> +#### 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/reference/directory_layout.adoc b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc index a50b699d461c..4291875a10c3 100644 --- a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc +++ b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc @@ -62,17 +62,46 @@ 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 occurred before proceeding. + +.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"] +==== [[dir:project_root]] == Project root directory 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..5241fd3fe5bd --- /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 --stacktrace -Dorg.gradle.debug=true" 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" From 4b78fe06e2501e64a4580808d5a7c1bcbfedfa96 Mon Sep 17 00:00:00 2001 From: Gary Hale Date: Wed, 7 Dec 2022 15:06:47 -0500 Subject: [PATCH 085/120] Fix issue with init script being called more than once in build session We can't finalize these properties, since this object could be used for more than one build during a build session. So instead, use a specialized property that allows it to only be configured during explicit periods when it is "unlocked". --- .../ConfigurationCacheState.kt | 4 +- .../cache/CacheConfigurationsInternal.java | 12 +- .../CacheResourceConfigurationInternal.java | 4 +- ...cheConfigurationsCompositeBuildTest.groovy | 56 +++++++ ...igurationsContinuousIntegrationTest.groovy | 60 ++++++++ .../CacheConfigurationsIntegrationTest.groovy | 17 +-- .../cache/DefaultCacheConfigurations.java | 143 +++++++++++++++--- .../ScriptEvaluatingSettingsProcessor.java | 3 +- .../DefaultCacheConfigurationsTest.groovy | 114 +++++++++++--- .../tests-kotlin/cacheRetention.sample.conf | 2 +- 10 files changed, 358 insertions(+), 57 deletions(-) create mode 100644 subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsCompositeBuildTest.groovy create mode 100644 subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsContinuousIntegrationTest.groovy 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..4ff54feb0d73 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 @@ -23,8 +23,8 @@ 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.CacheConfigurationsInternal.UnlockableProperty import org.gradle.api.internal.cache.CacheResourceConfigurationInternal -import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.services.internal.BuildServiceProvider import org.gradle.api.services.internal.BuildServiceRegistryInternal @@ -561,7 +561,7 @@ class ConfigurationCacheState( cacheConfigurations.snapshotWrappers = read() as CacheResourceConfigurationInternal? cacheConfigurations.downloadedResources = read() as CacheResourceConfigurationInternal? cacheConfigurations.createdResources = read() as CacheResourceConfigurationInternal? - cacheConfigurations.cleanup = readNonNull>() + cacheConfigurations.cleanup = readNonNull>() } } 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..3c396679b2c5 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,21 @@ public interface CacheConfigurationsInternal extends CacheConfigurations { @Override CacheResourceConfigurationInternal getCreatedResources(); + @Override + UnlockableProperty getCleanup(); + void setReleasedWrappers(CacheResourceConfigurationInternal releasedWrappers); void setSnapshotWrappers(CacheResourceConfigurationInternal snapshotWrappers); void setDownloadedResources(CacheResourceConfigurationInternal downloadedResources); void setCreatedResources(CacheResourceConfigurationInternal createdResources); - void setCleanup(Property cleanup); + void setCleanup(UnlockableProperty cleanup); - void finalizeConfigurations(); + void withMutableValues(Runnable runnable); Provider getCleanupFrequency(); + + interface UnlockableProperty extends Property { + void lock(); + 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/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..b23f72087684 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,8 +25,7 @@ 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" + private static final String CANNOT_CONFIGURE_MESSAGE = "You can only configure the property '%s' in an init script, preferably stored in the init.d directory inside the Gradle user home directory." def setup() { requireOwnGradleUserHomeDir() @@ -103,15 +102,15 @@ class CacheConfigurationsIntegrationTest extends AbstractIntegrationSpec { expect: fails("help") - failureCauseContains(error) + failureCauseContains(String.format(CANNOT_CONFIGURE_MESSAGE, 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/main/java/org/gradle/api/internal/cache/DefaultCacheConfigurations.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultCacheConfigurations.java index bc8872e94c3e..d788712bd40e 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,42 @@ import org.gradle.api.Action; import org.gradle.api.cache.CacheResourceConfiguration; import org.gradle.api.cache.Cleanup; +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.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 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"; + private CacheResourceConfigurationInternal releasedWrappersConfiguration; private CacheResourceConfigurationInternal snapshotWrappersConfiguration; private CacheResourceConfigurationInternal downloadedResourcesConfiguration; private CacheResourceConfigurationInternal createdResourcesConfiguration; - private Property cleanup; + private 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) { @@ -113,12 +124,12 @@ public void setCreatedResources(CacheResourceConfigurationInternal createdResour } @Override - public Property getCleanup() { + public UnlockableProperty getCleanup() { return cleanup; } @Override - public void setCleanup(Property cleanup) { + public void setCleanup(UnlockableProperty cleanup) { this.cleanup = cleanup; } @@ -128,12 +139,29 @@ public Provider getCleanupFrequency() { } @Override - public void finalizeConfigurations() { - releasedWrappersConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - snapshotWrappersConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - downloadedResourcesConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - createdResourcesConfiguration.getRemoveUnusedEntriesOlderThan().finalizeValue(); - getCleanup().finalizeValue(); + public void withMutableValues(Runnable runnable) { + unlockValues(); + try { + runnable.run(); + } finally { + lockValues(); + } + } + + private void lockValues() { + releasedWrappersConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + snapshotWrappersConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + downloadedResourcesConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + createdResourcesConfiguration.getRemoveUnusedEntriesOlderThan().lock(); + getCleanup().lock(); + } + + 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 +170,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 +201,78 @@ public void setRemoveUnusedEntriesAfterDays(int removeUnusedEntriesAfterDays) { getRemoveUnusedEntriesOlderThan().set(providerFromSupplier(daysAgo(removeUnusedEntriesAfterDays))); } } + + @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("You can only configure the property '" + getDisplayName() + "' in an init script, preferably stored in the init.d directory inside the Gradle user home directory. See "); + } + + private void onlyIfMutable(Runnable runnable) { + if (mutable) { + runnable.run(); + } else { + throw lockedError(); + } + } + + @Override + 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/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/test/groovy/org/gradle/api/internal/cache/DefaultCacheConfigurationsTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/cache/DefaultCacheConfigurationsTest.groovy index 9cf51f7a5ca5..0221b891fc64 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 @@ -23,51 +23,105 @@ import spock.lang.Specification import static org.gradle.internal.time.TimestampSuppliers.daysAgo class DefaultCacheConfigurationsTest extends Specification { + private static final String CANNOT_CONFIGURE_MESSAGE = "You can only configure the property '%s' in an init script, preferably stored in the init.d directory inside the Gradle user home directory." + 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.cleanup.set(Cleanup.DEFAULT) + 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: - thrown(IllegalStateException) + 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.snapshotWrappers.removeUnusedEntriesOlderThan."${method}"(secondValue) + + then: + 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 +133,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 +149,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 == String.format(CANNOT_CONFIGURE_MESSAGE, name) + } } 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 index 5241fd3fe5bd..b2dfb8ff3006 100644 --- a/subprojects/docs/src/snippets/initScripts/cacheRetention/tests-kotlin/cacheRetention.sample.conf +++ b/subprojects/docs/src/snippets/initScripts/cacheRetention/tests-kotlin/cacheRetention.sample.conf @@ -1,3 +1,3 @@ executable: gradle args: help -flags: "-I gradleUserHome/init.d/cache-settings.gradle.kts --stacktrace -Dorg.gradle.debug=true" +flags: "-I gradleUserHome/init.d/cache-settings.gradle.kts" From d4368a7b6a530a097fca5bc38eb29fc17fa7e067 Mon Sep 17 00:00:00 2001 From: Gary Hale Date: Thu, 8 Dec 2022 14:49:52 -0500 Subject: [PATCH 086/120] Add link to user guide to error meesage --- .../gradle/api/internal/cache/DefaultCacheConfigurations.java | 4 +++- .../api/internal/cache/DefaultCacheConfigurationsTest.groovy | 2 +- .../docs/src/docs/userguide/reference/directory_layout.adoc | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) 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 d788712bd40e..c8a7c7981e67 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,6 +19,7 @@ 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; @@ -40,6 +41,7 @@ abstract public class DefaultCacheConfigurations implements CacheConfigurationsI private static final String SNAPSHOT_WRAPPERS = "snapshotWrappers"; private static final String DOWNLOADED_RESOURCES = "downloadedResources"; private static final String CREATED_RESOURCES = "createdResources"; + private static final DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry(); private CacheResourceConfigurationInternal releasedWrappersConfiguration; private CacheResourceConfigurationInternal snapshotWrappersConfiguration; @@ -221,7 +223,7 @@ public void unlock() { } private IllegalStateException lockedError() { - return new IllegalStateException("You can only configure the property '" + getDisplayName() + "' in an init script, preferably stored in the init.d directory inside the Gradle user home directory. See "); + return new IllegalStateException("You can only configure the property '" + getDisplayName() + "' 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 void onlyIfMutable(Runnable runnable) { 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 0221b891fc64..7cc3df9cc8e7 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 @@ -182,6 +182,6 @@ class DefaultCacheConfigurationsTest extends Specification { } void assertCannotConfigureErrorIsThrown(Exception e, String name) { - assert e.message == String.format(CANNOT_CONFIGURE_MESSAGE, name) + assert e.message.contains(String.format(CANNOT_CONFIGURE_MESSAGE, name)) } } diff --git a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc index 4291875a10c3..5cb1039a1665 100644 --- a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc +++ b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc @@ -95,7 +95,7 @@ The frequency at which cache cleanup is invoked is also configurable. There are - *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 occurred before proceeding. +- *ALWAYS:* Cleanup is performed at the end of each build session. This is useful in cases where it's desirable to ensure that cleanup has actually occurred before proceeding. However, this does perform cache cleanup during the build (rather than in the background) which can be an expensive operation, so this option should only be used when absolutely necessary. .Disabling cache cleanup ==== @@ -103,6 +103,8 @@ include::sample[dir="snippets/initScripts/disableCacheCleanup/groovy",files="gra 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 From 72ed06fc92237343b83565827a9d883f0116a292 Mon Sep 17 00:00:00 2001 From: Gary Hale Date: Thu, 8 Dec 2022 16:31:44 -0500 Subject: [PATCH 087/120] Fix configuration cache encoding for UnlockableProperty --- .../ConfigurationCacheState.kt | 27 ++++++++------ .../cache/CacheConfigurationsInternal.java | 6 ---- .../cache/DefaultCacheConfigurations.java | 35 +++---------------- 3 files changed, 21 insertions(+), 47 deletions(-) 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 4ff54feb0d73..ce6868dd6f1a 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.CacheConfigurationsInternal.UnlockableProperty -import org.gradle.api.internal.cache.CacheResourceConfigurationInternal import org.gradle.api.provider.Provider import org.gradle.api.services.internal.BuildServiceProvider import org.gradle.api.services.internal.BuildServiceRegistryInternal @@ -546,10 +544,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 +555,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 -> 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 3c396679b2c5..a5a1893b50c7 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 @@ -40,12 +40,6 @@ public interface CacheConfigurationsInternal extends CacheConfigurations { @Override UnlockableProperty getCleanup(); - void setReleasedWrappers(CacheResourceConfigurationInternal releasedWrappers); - void setSnapshotWrappers(CacheResourceConfigurationInternal snapshotWrappers); - void setDownloadedResources(CacheResourceConfigurationInternal downloadedResources); - void setCreatedResources(CacheResourceConfigurationInternal createdResources); - void setCleanup(UnlockableProperty cleanup); - void withMutableValues(Runnable runnable); Provider getCleanupFrequency(); 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 c8a7c7981e67..b3c04429f12d 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 @@ -43,11 +43,11 @@ abstract public class DefaultCacheConfigurations implements CacheConfigurationsI private static final String CREATED_RESOURCES = "createdResources"; private static final DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry(); - private CacheResourceConfigurationInternal releasedWrappersConfiguration; - private CacheResourceConfigurationInternal snapshotWrappersConfiguration; - private CacheResourceConfigurationInternal downloadedResourcesConfiguration; - private CacheResourceConfigurationInternal createdResourcesConfiguration; - private UnlockableProperty cleanup; + 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, PropertyHost propertyHost) { @@ -75,11 +75,6 @@ public CacheResourceConfigurationInternal getReleasedWrappers() { return releasedWrappersConfiguration; } - @Override - public void setReleasedWrappers(CacheResourceConfigurationInternal releasedWrappers) { - this.releasedWrappersConfiguration = releasedWrappers; - } - @Override public void snapshotWrappers(Action cacheConfiguration) { cacheConfiguration.execute(snapshotWrappersConfiguration); @@ -90,11 +85,6 @@ public CacheResourceConfigurationInternal getSnapshotWrappers() { return snapshotWrappersConfiguration; } - @Override - public void setSnapshotWrappers(CacheResourceConfigurationInternal snapshotWrappers) { - this.snapshotWrappersConfiguration = snapshotWrappers; - } - @Override public void downloadedResources(Action cacheConfiguration) { cacheConfiguration.execute(downloadedResourcesConfiguration); @@ -105,11 +95,6 @@ public CacheResourceConfigurationInternal getDownloadedResources() { return downloadedResourcesConfiguration; } - @Override - public void setDownloadedResources(CacheResourceConfigurationInternal downloadedResources) { - this.downloadedResourcesConfiguration = downloadedResources; - } - @Override public void createdResources(Action cacheConfiguration) { cacheConfiguration.execute(createdResourcesConfiguration); @@ -120,21 +105,11 @@ public CacheResourceConfigurationInternal getCreatedResources() { return createdResourcesConfiguration; } - @Override - public void setCreatedResources(CacheResourceConfigurationInternal createdResources) { - this.createdResourcesConfiguration = createdResources; - } - @Override public UnlockableProperty getCleanup() { return cleanup; } - @Override - public void setCleanup(UnlockableProperty cleanup) { - this.cleanup = cleanup; - } - @Override public Provider getCleanupFrequency() { return getCleanup().map(cleanup -> ((CleanupInternal)cleanup).getCleanupFrequency()); From e38eb8094c075ce23f4d840f2bc92b688088af26 Mon Sep 17 00:00:00 2001 From: Gary Hale Date: Fri, 9 Dec 2022 09:14:50 -0500 Subject: [PATCH 088/120] Polish some javadocs and error messages --- .../cache/CacheConfigurationsInternal.java | 16 ++++++++++++++++ .../CacheConfigurationsIntegrationTest.groovy | 4 +--- .../cache/DefaultCacheConfigurations.java | 10 ++++++++-- .../cache/DefaultCacheConfigurationsTest.groovy | 4 +--- 4 files changed, 26 insertions(+), 8 deletions(-) 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 a5a1893b50c7..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 @@ -40,12 +40,28 @@ public interface CacheConfigurationsInternal extends CacheConfigurations { @Override UnlockableProperty getCleanup(); + /** + * 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/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/cache/CacheConfigurationsIntegrationTest.groovy index b23f72087684..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,8 +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 CANNOT_CONFIGURE_MESSAGE = "You can only configure the property '%s' in an init script, preferably stored in the init.d directory inside the Gradle user home directory." - def setup() { requireOwnGradleUserHomeDir() } @@ -102,7 +100,7 @@ class CacheConfigurationsIntegrationTest extends AbstractIntegrationSpec { expect: fails("help") - failureCauseContains(String.format(CANNOT_CONFIGURE_MESSAGE, errorProperty)) + failureCauseContains(String.format(DefaultCacheConfigurations.ILLEGAL_MODIFICATION_ERROR, errorProperty)) where: property | errorProperty | value 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 b3c04429f12d..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 @@ -29,6 +29,7 @@ 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; @@ -37,11 +38,12 @@ import static org.gradle.internal.time.TimestampSuppliers.daysAgo; abstract public class DefaultCacheConfigurations implements CacheConfigurationsInternal { + 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"; - private static final DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry(); + 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; @@ -179,6 +181,9 @@ public void setRemoveUnusedEntriesAfterDays(int 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; @@ -198,7 +203,7 @@ public void unlock() { } private IllegalStateException lockedError() { - return new IllegalStateException("You can only configure the property '" + getDisplayName() + "' 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."); + return new IllegalStateException(String.format(ILLEGAL_MODIFICATION_ERROR, getDisplayName())); } private void onlyIfMutable(Runnable runnable) { @@ -210,6 +215,7 @@ private void onlyIfMutable(Runnable runnable) { } @Override + @Nonnull protected DisplayName getDisplayName() { if (displayName != null) { return Describables.of(displayName); 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 7cc3df9cc8e7..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 @@ -23,8 +23,6 @@ import spock.lang.Specification import static org.gradle.internal.time.TimestampSuppliers.daysAgo class DefaultCacheConfigurationsTest extends Specification { - private static final String CANNOT_CONFIGURE_MESSAGE = "You can only configure the property '%s' in an init script, preferably stored in the init.d directory inside the Gradle user home directory." - def cacheConfigurations = TestUtil.objectFactory().newInstance(DefaultCacheConfigurations.class) def "cannot modify cache configurations via convenience method unless mutable"() { @@ -182,6 +180,6 @@ class DefaultCacheConfigurationsTest extends Specification { } void assertCannotConfigureErrorIsThrown(Exception e, String name) { - assert e.message.contains(String.format(CANNOT_CONFIGURE_MESSAGE, name)) + assert e.message.contains(String.format(DefaultCacheConfigurations.ILLEGAL_MODIFICATION_ERROR, name)) } } From fac9a9b9bf4e88baff01af7817b33dc70a114cb8 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 12 Dec 2022 14:47:14 -0500 Subject: [PATCH 089/120] Update test utils fixture with new cache cleanup strategy method --- .../groovy/org/gradle/cache/internal/TestCaches.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 index ed1cd89e73a8..c1d0fdf52cdd 100644 --- a/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java +++ b/subprojects/core/src/testFixtures/groovy/org/gradle/cache/internal/TestCaches.java @@ -17,7 +17,9 @@ package org.gradle.cache.internal; import org.gradle.api.Action; +import org.gradle.api.internal.cache.DefaultCacheCleanupStrategy; import org.gradle.cache.CacheBuilder; +import org.gradle.cache.CacheCleanupStrategy; import org.gradle.cache.CacheOpenException; import org.gradle.cache.CleanupAction; import org.gradle.cache.LockOptions; @@ -134,7 +136,7 @@ private static final class TestInMemoryCacheBuilder implements CacheBuilder { private String displayName = "Test In Memory Cache"; private LockOptions lockOptions = mode(OnDemand); private Action initializer = Actions.doNothing(); - private CleanupAction cleanup = CleanupAction.NO_OP; + private CacheCleanupStrategy cacheCleanupStrategy = DefaultCacheCleanupStrategy.from(CleanupAction.NO_OP); private TestInMemoryCacheBuilder(File cacheDir) { this.cacheDir = cacheDir; @@ -171,14 +173,14 @@ public CacheBuilder withInitializer(Action initializer) } @Override - public CacheBuilder withCleanup(CleanupAction cleanup) { - this.cleanup = cleanup; + public CacheBuilder withCleanupStrategy(CacheCleanupStrategy cacheCleanupStrategy) { + this.cacheCleanupStrategy = cacheCleanupStrategy; return this; } @Override public PersistentCache open() throws CacheOpenException { - return cacheFactory.open(cacheDir, displayName, properties, lockTarget, lockOptions, initializer, cleanup); + return cacheFactory.open(cacheDir, displayName, properties, lockTarget, lockOptions, initializer, cacheCleanupStrategy); } } } From d8774f30d4c02831c0e354c156ef50e692c8912a Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Tue, 13 Dec 2022 00:50:52 +0500 Subject: [PATCH 090/120] Make all lambdas serializable when configuration cache is enabled --- ...igurationCacheLambdaIntegrationTest.groovy | 29 ++++++++++++++----- .../tasks/LambdaInputsIntegrationTest.groovy | 8 +++++ .../classpath/InstrumentingTransformer.java | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) 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..b29abdc2b77a 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 setSerializableSupplier(SerializableSupplier supplier) { + this.serializableSupplier = supplier; + } - public void setSupplier(SerializableSupplier supplier) { - this.supplier = 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 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/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java index 0f804fcb7ddb..73281a7b0f13 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 @@ -524,7 +524,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, From 1f038da02134d01e21f7d83ea9c5d4ee269216c5 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Wed, 7 Dec 2022 03:50:35 +0500 Subject: [PATCH 091/120] Fix lambda serialization instrumentation clash on same method names If there are multiple lambdas in a class with their implementation methods having the same name, then, in `$deserialize_lambda$` codegen, distinguishing those lambdas just by name is not enough. We need to also check for the implementation method descriptors. --- ...igurationCacheLambdaIntegrationTest.groovy | 49 +++++++++++++++++++ .../classpath/InstrumentingTransformer.java | 11 ++++- 2 files changed, 59 insertions(+), 1 deletion(-) 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 b29abdc2b77a..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 @@ -207,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/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java index 73281a7b0f13..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); From 2e5ff6c66af01597518762c292b63045cf796b39 Mon Sep 17 00:00:00 2001 From: Thomas Tresansky Date: Mon, 12 Dec 2022 16:47:46 -0500 Subject: [PATCH 092/120] Add some further tests for behavior with multiple archives sharing the cache --- .../bundling/ArchiveIntegrationTest.groovy | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) 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 3911474badb9..e6726066178f 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 @@ -26,6 +26,7 @@ import org.gradle.test.fixtures.archive.TarTestFixture import org.gradle.test.fixtures.archive.ZipTestFixture import org.gradle.test.fixtures.file.TestFile import org.gradle.test.fixtures.server.http.BlockingHttpServer +import org.gradle.util.GradleVersion import org.hamcrest.CoreMatchers import org.junit.Rule import spock.lang.IgnoreIf @@ -1259,6 +1260,192 @@ class ArchiveIntegrationTest extends AbstractIntegrationSpec { server.stop() } + def "can operate on 2 different tar files in the same project using file operations: #foSource"() { + 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', foSource)} + ${defineVerifyTask('tar', foSource)} + + 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') + + where: + foSource << [FileOperationsSourceType.INJECT_ARCHIVE_OPS, FileOperationsSourceType.PROJECT_FILE_OPS] + } + + def "can operate on 2 different zip files in the same project using file operations: #foSource"() { + 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', foSource)} + ${defineVerifyTask('zip', foSource)} + + 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') + + where: + foSource << [FileOperationsSourceType.INJECT_ARCHIVE_OPS, FileOperationsSourceType.PROJECT_FILE_OPS] + } + + def "when two identical archives have the same hashes and 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', foSource)} + ${defineVerifyTask('tar', foSource)} + + 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 = project.layout.buildDirectory.dir('.cache/${GradleVersion.current().version}/compressed-file-expansion').get().asFile + cacheDir.list().size() == 2 // There should only be 2 files here, the .lock file and the single unzipped cache entry + cacheDir.list().contains('compressed-file-expansion.lock') + cacheDir.eachFile(groovy.io.FileType.DIRECTORIES) { File f -> + assert f.name.startsWith('tar_') + } + } + } + """ + + when: + run 'verify' + + then: + result.assertTasksExecutedAndNotSkipped(':update1', ':update2', ':verify') + + where: + foSource << [FileOperationsSourceType.INJECT_ARCHIVE_OPS, FileOperationsSourceType.PROJECT_FILE_OPS] + } + private def createTar(String name, Closure cl) { TestFile tarRoot = file("${name}.root") tarRoot.deleteDir() From ca7079c84bcda6a223b265c67001e7f347b5fa2c Mon Sep 17 00:00:00 2001 From: Adam Murdoch Date: Tue, 6 Sep 2022 08:35:47 +1000 Subject: [PATCH 093/120] Apply init scripts to `buildSrc` build, for consistency with included builds. --- .../gradle/composite/internal/BuildStateFactory.java | 2 +- .../initialization/InitScriptIntegrationTest.groovy | 3 +-- .../gradle/DeprecationHandlingIntegrationTest.groovy | 11 +++++++---- ...nfigurationChildrenProgressCrossVersionSpec.groovy | 4 +++- 4 files changed, 12 insertions(+), 8 deletions(-) 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/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/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/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) } From 59f43eb9c66f8d658bdd6269cb05b33b194ee1bc Mon Sep 17 00:00:00 2001 From: Adam Murdoch Date: Fri, 9 Sep 2022 16:13:37 +1000 Subject: [PATCH 094/120] Allow tasks from `buildSrc` to be requested from the command-line, in the same way tasks from included builds can be requested. Defer running user build finished hooks from `buildSrc` until the main work graph has completed. --- ...siteBuildTaskExcludeIntegrationTest.groovy | 5 +- .../internal/DefaultNestedBuild.java | 16 ++- .../internal/DefaultNestedBuildTest.groovy | 27 +---- .../configurationcache/CachedBuildState.kt | 10 +- .../ConfigurationCacheBuild.kt | 7 +- .../ConfigurationCacheHost.kt | 22 ++-- .../ConfigurationCacheState.kt | 114 ++++++++++++------ .../DefaultConfigurationCache.kt | 4 +- .../BuildSrcEventsIntegrationTest.groovy | 28 +++-- ...uildSrcTaskExecutionIntegrationTest.groovy | 91 ++++++++++++++ .../selection/DefaultBuildTaskSelector.java | 20 +-- .../internal/build/BuildStateRegistry.java | 4 +- .../internal/build/StandAloneNestedBuild.java | 1 + .../DefaultBuildTaskSelectorTest.groovy | 15 ++- subprojects/docs/src/docs/release/notes.md | 6 + .../migration/upgrading_version_7.adoc | 12 +- ...ractConsoleBuildPhaseFunctionalTest.groovy | 9 -- 17 files changed, 273 insertions(+), 118 deletions(-) create mode 100644 subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcTaskExecutionIntegrationTest.groovy 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/DefaultNestedBuild.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultNestedBuild.java index 0147ad39fe3b..89db73ebb7d1 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 @@ -64,15 +64,8 @@ class DefaultNestedBuild extends AbstractBuildState implements StandAloneNestedB 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 +89,11 @@ public boolean isImplicitBuild() { return true; } + @Override + public BuildState getOwner() { + return owner; + } + @Override public ExecutionResult finishBuild() { return getBuildController().finishBuild(null); 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/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 ce6868dd6f1a..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 @@ -61,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 @@ -206,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( @@ -232,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() } } @@ -252,6 +262,7 @@ class ConfigurationCacheState( BuildType.BuildWithNoWork -> readBuildWithNoWork(rootBuild) BuildType.RootBuild -> readBuildContent(rootBuild) BuildType.IncludedBuild -> readIncludedBuild(rootBuild) + BuildType.BuildSrcBuild -> readBuildSrcBuild(rootBuild) } } @@ -270,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 -> @@ -313,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 @@ -750,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 } @@ -761,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/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/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/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/internal/build/BuildStateRegistry.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildStateRegistry.java index 7453d51c6c93..cf7b2cdb1ade 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,7 +66,7 @@ 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(); 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/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/docs/src/docs/release/notes.md b/subprojects/docs/src/docs/release/notes.md index 152b44c58459..8846d6c609fb 100644 --- a/subprojects/docs/src/docs/release/notes.md +++ b/subprojects/docs/src/docs/release/notes.md @@ -251,6 +251,12 @@ In Java projects, these tools will use the same version of Java required by the ### IDE Integration +#### Run `buildSrc` tasks +TODO - Can now run the tasks of `buildSrc` from the command-line, using the same syntax as tasks of included builds, eg `gradle buildSrc:build`. + +### Init scripts are applied to `buildSrc` +TODO - init scripts are applied to `buildSrc`, which is the same for included builds. +