diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java index d06af37ed9531..46755c58ecb08 100644 --- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java +++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java @@ -17,6 +17,7 @@ import groovy.lang.Closure; import org.gradle.api.Action; +import org.gradle.api.Incubating; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.MinimalExternalModuleDependency; @@ -383,6 +384,15 @@ public interface DependencyHandler extends ExtensionAware { */ Dependency gradleApi(); + /** + * Creates a dependency on the API of the specified version of Gradle. + * + * @return The dependency. + * @since 7.5 + */ + @Incubating + Dependency gradleApi(String version); + /** * Creates a dependency on the Gradle test-kit API. * diff --git a/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/GroovyImportsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/GroovyImportsIntegrationTest.groovy index 5bb0b875fe40c..c008afea0d6fb 100644 --- a/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/GroovyImportsIntegrationTest.groovy +++ b/subprojects/core/src/integTest/groovy/org/gradle/groovy/scripts/GroovyImportsIntegrationTest.groovy @@ -38,5 +38,4 @@ import org.apache.commons.math3.util.FastMath args("--init-script", initScript.toString()) succeeds("help") } - } diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/GradleApiVersionProvider.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/GradleApiVersionProvider.java new file mode 100644 index 0000000000000..868e9d6b833b6 --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/GradleApiVersionProvider.java @@ -0,0 +1,68 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.api.internal.artifacts; + +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.SelfResolvingDependency; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.artifacts.dsl.RepositoryHandler; + +import java.io.File; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +public class GradleApiVersionProvider { + + public static final String GRADLE_API_SOURCE_VERSION_PROPERTY = "org.gradle.api.source-version"; + + public static Optional getGradleApiSourceVersion() { + return Optional.ofNullable(System.getProperty(GRADLE_API_SOURCE_VERSION_PROPERTY)); + } + + public static void addGradleSourceApiRepository(RepositoryHandler repositoryHandler) { + getGradleApiSourceVersion().ifPresent(version -> { + String repositoryUrl = System.getProperty("gradle.api.repository.url", "https://repo.gradle.org/gradle/libs-releases"); + repositoryHandler.maven(repo -> repo.setUrl(repositoryUrl)); + }); + } + + public static void addToConfiguration(Configuration configuration, DependencyHandler repositoryHandler) { + Dependency gradleApiDependency = getGradleApiSourceVersion() + .map(repositoryHandler::gradleApi) + .orElseGet(repositoryHandler::gradleApi); + configuration.getDependencies().add(gradleApiDependency); + } + + public static Collection resolveGradleSourceApi(DependencyResolutionServices dependencyResolutionServices) { + return getGradleApiSourceVersion() + .map(version -> gradleApisFromRepository(dependencyResolutionServices, version)) + .orElseGet(() -> gradleApisFromCurrentGradle(dependencyResolutionServices.getDependencyHandler())); + } + + private static Set gradleApisFromCurrentGradle(DependencyHandler dependencyHandler) { + SelfResolvingDependency gradleApiDependency = (SelfResolvingDependency) dependencyHandler.gradleApi(); + return gradleApiDependency.resolve(); + + } + private static Set gradleApisFromRepository(DependencyResolutionServices dependencyResolutionServices, String version) { + addGradleSourceApiRepository(dependencyResolutionServices.getResolveRepositoryHandler()); + Configuration detachedConfiguration = dependencyResolutionServices.getConfigurationContainer().detachedConfiguration(dependencyResolutionServices.getDependencyHandler().gradleApi(version)); + return detachedConfiguration.resolve(); + } +} diff --git a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java index f317c1c449012..a8550ede4a8ba 100644 --- a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java +++ b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/DefaultScriptCompilationHandler.java @@ -39,6 +39,7 @@ import org.gradle.groovy.scripts.Transformer; import org.gradle.internal.UncheckedException; import org.gradle.internal.classloader.ClassLoaderUtils; +import org.gradle.internal.classloader.FilteringClassLoader; import org.gradle.internal.classloader.ImplementationHashAware; import org.gradle.internal.classloader.VisitableURLClassLoader; import org.gradle.internal.classpath.ClassPath; @@ -107,18 +108,43 @@ public void compileToDir(ScriptSource source, ClassLoader classLoader, File clas } private void compileScript(ScriptSource source, ClassLoader classLoader, CompilerConfiguration configuration, File metadataDir, - final CompileOperation extractingTransformer, final Action customVerifier) { + final CompileOperation extractingTransformer, final Action customVerifier + ) { final Transformer transformer = extractingTransformer != null ? extractingTransformer.getTransformer() : null; logger.info("Compiling {} using {}.", source.getDisplayName(), transformer != null ? transformer.getClass().getSimpleName() : "no transformer"); final EmptyScriptDetector emptyScriptDetector = new EmptyScriptDetector(); final PackageStatementDetector packageDetector = new PackageStatementDetector(); + + FilteringClassLoader.Spec groovyCompilerClassLoaderSpec = new FilteringClassLoader.Spec(); + groovyCompilerClassLoaderSpec.allowPackage("org.codehaus.groovy"); + groovyCompilerClassLoaderSpec.allowPackage("groovy"); + groovyCompilerClassLoaderSpec.allowPackage("groovyjarjarasm"); + // Disallow classes from Groovy Jar that reference external classes. Such classes must be loaded from astTransformClassLoader, + // or a NoClassDefFoundError will occur. Essentially this is drawing a line between the Groovy compiler and the Groovy + // library, albeit only for selected classes that run a high risk of being statically referenced from a transform. + groovyCompilerClassLoaderSpec.disallowClass("groovy.util.GroovyTestCase"); + groovyCompilerClassLoaderSpec.disallowClass("org.codehaus.groovy.transform.NotYetImplementedASTTransformation"); + groovyCompilerClassLoaderSpec.disallowPackage("groovy.servlet"); + FilteringClassLoader groovyCompilerClassLoader = new FilteringClassLoader(GroovyClassLoader.class.getClassLoader(), groovyCompilerClassLoaderSpec); + + // AST transforms need their own class loader that shares compiler classes with the compiler itself + // can't delegate to compileClasspathLoader because this would result in ASTTransformation interface + // (which is implemented by the transform class) being loaded by compileClasspathClassLoader (which is + // where the transform class is loaded from) + GroovyClassLoader astTransformClassLoader = new GroovyClassLoader(groovyCompilerClassLoader, null); + GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader, configuration, false) { @Override protected CompilationUnit createCompilationUnit(CompilerConfiguration compilerConfiguration, CodeSource codeSource) { - CompilationUnit compilationUnit = new CustomCompilationUnit(compilerConfiguration, codeSource, customVerifier, this, simpleNameToFQN); + CompilationUnit compilationUnit = new CustomCompilationUnit(compilerConfiguration, codeSource, customVerifier, this, simpleNameToFQN) { + @Override + public GroovyClassLoader getTransformLoader() { + return astTransformClassLoader; + } + }; if (transformer != null) { transformer.register(compilationUnit); diff --git a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java index 264262fbff0bf..3d77f41de2103 100644 --- a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java +++ b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java @@ -50,6 +50,7 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.net.URLClassLoader; import static org.gradle.internal.classpath.CachedClasspathTransformer.StandardTransform.BuildLogic; @@ -63,16 +64,22 @@ public class FileCacheBackedScriptClassCompiler implements ScriptClassCompiler, private final GlobalScopedCache cacheRepository; private final ClassLoaderHierarchyHasher classLoaderHierarchyHasher; private final CachedClasspathTransformer classpathTransformer; + private final GroovyScriptClasspathProvider groovyScriptClasspathProvider; public FileCacheBackedScriptClassCompiler( - GlobalScopedCache cacheRepository, ScriptCompilationHandler scriptCompilationHandler, - ProgressLoggerFactory progressLoggerFactory, ClassLoaderHierarchyHasher classLoaderHierarchyHasher, - CachedClasspathTransformer classpathTransformer) { + GlobalScopedCache cacheRepository, + ScriptCompilationHandler scriptCompilationHandler, + ProgressLoggerFactory progressLoggerFactory, + ClassLoaderHierarchyHasher classLoaderHierarchyHasher, + CachedClasspathTransformer classpathTransformer, + GroovyScriptClasspathProvider groovyScriptClasspathProvider + ) { this.cacheRepository = cacheRepository; this.scriptCompilationHandler = scriptCompilationHandler; this.progressLoggerFactory = progressLoggerFactory; this.classLoaderHierarchyHasher = classLoaderHierarchyHasher; this.classpathTransformer = classpathTransformer; + this.groovyScriptClasspathProvider = groovyScriptClasspathProvider; } @Override @@ -80,13 +87,16 @@ public CompiledScript compile(final ScriptSource sou final ClassLoaderScope targetScope, final CompileOperation operation, final Class scriptBaseClass, - final Action verifier) { + final Action verifier + ) { assert source.getResource().isContentCached(); if (source.getResource().getHasEmptyContent()) { return emptyCompiledScript(operation); } ClassLoader classLoader = targetScope.getExportClassLoader(); + ClassPath compilationClasspath = groovyScriptClasspathProvider.compilationClassPathOf(targetScope); + ClassLoader compileClassLoader = new URLClassLoader(compilationClasspath.getAsURLArray(), ClassLoader.getSystemClassLoader().getParent()); HashCode sourceHashCode = source.getResource().getContentHash(); final String dslId = operation.getId(); HashCode classLoaderHash = classLoaderHierarchyHasher.getClassLoaderHash(classLoader); @@ -99,6 +109,10 @@ public CompiledScript compile(final ScriptSource sou hasher.putString(dslId); hasher.putHash(sourceHashCode); hasher.putHash(classLoaderHash); + for (File jar : compilationClasspath.getAsFiles()) { + // TODO: Snapshot the contents here. + hasher.putString(jar.getName()); + } String key = hasher.hash().toCompactString(); // Caching involves 2 distinct caches, so that 2 scripts with the same (hash, classpath) do not get compiled twice @@ -111,7 +125,7 @@ public CompiledScript compile(final ScriptSource sou .withDisplayName(dslId + " generic class cache for " + source.getDisplayName()) .withInitializer(new ProgressReportingInitializer( progressLoggerFactory, - new CompileToCrossBuildCacheAction(remapped, classLoader, operation, verifier, scriptBaseClass), + new CompileToCrossBuildCacheAction(remapped, compileClassLoader, operation, verifier, scriptBaseClass), "Compiling " + source.getShortDisplayName())) .open(); try { diff --git a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/GroovyScriptClasspathProvider.java b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/GroovyScriptClasspathProvider.java new file mode 100644 index 0000000000000..e6111fb8fb36b --- /dev/null +++ b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/GroovyScriptClasspathProvider.java @@ -0,0 +1,123 @@ +/* + * 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.groovy.scripts.internal; + +import org.gradle.api.internal.initialization.ClassLoaderScope; +import org.gradle.internal.classloader.ClassLoaderVisitor; +import org.gradle.internal.classpath.ClassPath; +import org.gradle.internal.classpath.DefaultClassPath; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; + +public class GroovyScriptClasspathProvider { + private final ConcurrentMap cachedScopeCompilationClasspath = new ConcurrentHashMap<>(); + private final Map> cachedClasspaths = new HashMap<>(); + private final Set gradleImplementationClasspath = new LinkedHashSet<>(); + private ClassPath gradleApi; + private ClassPath groovyJars; + private final Supplier> gradleApiJarsProvider; + private final Supplier> groovyJarsProvider; + + public GroovyScriptClasspathProvider( + ClassLoaderScope coreAndPluginsScope, + Supplier> gradleApiJarsProvider, + Supplier> groovyJarsProvider + ) { + this.gradleApiJarsProvider = gradleApiJarsProvider; + this.groovyJarsProvider = groovyJarsProvider; + this.gradleImplementationClasspath.addAll(classpathOf(coreAndPluginsScope.getExportClassLoader())); + this.gradleImplementationClasspath.removeIf(file -> !file.getName().startsWith("gradle-")); + } + + public ClassPath compilationClassPathOf(ClassLoaderScope scope) { + return getGradleApi().plus(exportClassPathFromHierarchyOf(scope)); + } + + private ClassPath getGradleApi() { + if (gradleApi == null) { + gradleApi = DefaultClassPath.of(gradleApiJarsProvider.get()); + } + return gradleApi; + } + + private ClassPath getGroovy() { + if (groovyJars == null) { + groovyJars = DefaultClassPath.of(groovyJarsProvider.get()); + } + return groovyJars; + } + + private ClassPath exportClassPathFromHierarchyOf(ClassLoaderScope scope) { + Set exportedClasspath = new LinkedHashSet<>(classpathOf(scope.getExportClassLoader())); + exportedClasspath.removeAll(gradleImplementationClasspath); + return DefaultClassPath.of(exportedClasspath); + } + + private Set classpathOf(ClassLoader classLoader) { + Set classpath = cachedClasspaths.get(classLoader); + if (classpath == null) { + Set classpathFiles = new LinkedHashSet<>(); + new ClassLoaderVisitor() { + @Override + public void visitClassPath(URL[] classPath) { + for (URL url : classPath) { + if (url.getProtocol().equals("file")) { + classpathFiles.add(new File(toUri(url))); + } + } + } + + @Override + public void visitParent(ClassLoader parent) { + classpathFiles.addAll(classpathOf(parent)); + } + + private URI toUri(URL url) { + try { + return url.toURI(); + } catch (URISyntaxException e) { + try { + return new URL( + url.getProtocol(), + url.getHost(), + url.getPort(), + url.getFile().replace(" ", "%20") + ).toURI(); + } catch (URISyntaxException | MalformedURLException ex) { + throw new RuntimeException(ex); + } + } + } + }.visit(classLoader); + cachedClasspaths.put(classLoader, classpathFiles); + classpath = classpathFiles; + } + return classpath; + } +} diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java index 53682561019d6..ad1c9f17ab702 100644 --- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java +++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/BuildScopeServices.java @@ -18,6 +18,7 @@ import org.gradle.StartParameter; import org.gradle.api.Project; +import org.gradle.api.artifacts.SelfResolvingDependency; import org.gradle.api.internal.BuildDefinition; import org.gradle.api.internal.ClassPathRegistry; import org.gradle.api.internal.DefaultClassPathProvider; @@ -29,8 +30,12 @@ import org.gradle.api.internal.StartParameterInternal; import org.gradle.api.internal.artifacts.DefaultModule; import org.gradle.api.internal.artifacts.DependencyManagementServices; +import org.gradle.api.internal.artifacts.DependencyResolutionServices; +import org.gradle.api.internal.artifacts.GradleApiVersionProvider; import org.gradle.api.internal.artifacts.Module; import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider; +import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory; +import org.gradle.api.internal.artifacts.dsl.dependencies.UnknownProjectFinder; import org.gradle.api.internal.classpath.ModuleRegistry; import org.gradle.api.internal.classpath.PluginModuleRegistry; import org.gradle.api.internal.component.ComponentTypeRegistry; @@ -43,6 +48,7 @@ import org.gradle.api.internal.file.temp.TemporaryFileProvider; import org.gradle.api.internal.initialization.DefaultScriptClassPathResolver; import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory; +import org.gradle.api.internal.initialization.RootScriptDomainObjectContext; import org.gradle.api.internal.initialization.ScriptClassPathInitializer; import org.gradle.api.internal.initialization.ScriptClassPathResolver; import org.gradle.api.internal.initialization.ScriptHandlerFactory; @@ -117,6 +123,7 @@ import org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler; import org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory; import org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler; +import org.gradle.groovy.scripts.internal.GroovyScriptClasspathProvider; import org.gradle.groovy.scripts.internal.ScriptRunnerFactory; import org.gradle.initialization.BuildLoader; import org.gradle.initialization.BuildOperationFiringSettingsPreparer; @@ -416,6 +423,27 @@ protected ITaskFactory createITaskFactory(Instantiator instantiator, TaskClassIn new TaskFactory()); } + protected GroovyScriptClasspathProvider createGroovyScriptClasspathProvider( + ClassLoaderScopeRegistry classLoaderScopeRegistry, + DependencyManagementServices dependencyManagementServices, + FileResolver fileResolver, + FileCollectionFactory fileCollectionFactory, + DependencyMetaDataProvider dependencyMetaDataProvider, + DependencyFactory dependencyFactory + ) { + DependencyResolutionServices resolutionServices = dependencyManagementServices.create( + fileResolver, + fileCollectionFactory, + dependencyMetaDataProvider, + new UnknownProjectFinder("Some unknown project"), + RootScriptDomainObjectContext.PLUGINS + ); + return new GroovyScriptClasspathProvider( + classLoaderScopeRegistry.getCoreAndPluginsScope(), + () -> GradleApiVersionProvider.resolveGradleSourceApi(resolutionServices), + () -> ((SelfResolvingDependency) dependencyFactory.createDependency(DependencyFactory.ClassPathNotation.LOCAL_GROOVY)).resolve()); + } + protected ScriptCompilerFactory createScriptCompileFactory( FileCacheBackedScriptClassCompiler scriptCompiler, CrossBuildInMemoryCachingScriptClassCache cache, @@ -433,14 +461,16 @@ protected FileCacheBackedScriptClassCompiler createFileCacheBackedScriptClassCom ClassLoaderHierarchyHasher classLoaderHierarchyHasher, DefaultScriptCompilationHandler scriptCompilationHandler, CachedClasspathTransformer classpathTransformer, - ProgressLoggerFactory progressLoggerFactory + ProgressLoggerFactory progressLoggerFactory, + GroovyScriptClasspathProvider groovyScriptClasspathProvider ) { return new FileCacheBackedScriptClassCompiler( cacheRepository, new BuildOperationBackedScriptCompilationHandler(scriptCompilationHandler, buildOperationExecutor), progressLoggerFactory, classLoaderHierarchyHasher, - classpathTransformer); + classpathTransformer, + groovyScriptClasspathProvider); } protected ScriptPluginFactory createScriptPluginFactory(InstantiatorFactory instantiatorFactory, BuildOperationExecutor buildOperationExecutor, UserCodeApplicationContext userCodeApplicationContext) { diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java index 419a26b5fdd84..c068cf075bee6 100644 --- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java +++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultDependencyHandler.java @@ -266,6 +266,11 @@ public Dependency gradleApi() { return dependencyFactory.createDependency(DependencyFactory.ClassPathNotation.GRADLE_API); } + @Override + public Dependency gradleApi(String version) { + return dependencyFactory.createDependency("org.gradle:gradle-api:" + version); + } + @Override public Dependency gradleTestKit() { return dependencyFactory.createDependency(DependencyFactory.ClassPathNotation.GRADLE_TEST_KIT); diff --git a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt index dd3d8fbbf119f..2cc4fc063988c 100644 --- a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt +++ b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt @@ -19,6 +19,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.internal.artifacts.GradleApiVersionProvider import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.TaskProvider @@ -44,8 +45,9 @@ class PrecompiledScriptPlugins : Plugin { dependencies { "kotlinCompilerPluginClasspath"(gradleKotlinDslJarsOf(project)) - "kotlinCompilerPluginClasspath"(gradleApi()) } + GradleApiVersionProvider.addToConfiguration(configurations.getByName("kotlinCompilerPluginClasspath"), dependencies) + GradleApiVersionProvider.addGradleSourceApiRepository(repositories) } } diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt index 95ead239d8ac3..5bdf4d1f6a4d4 100644 --- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt +++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt @@ -23,6 +23,7 @@ import org.gradle.api.file.Directory import org.gradle.api.file.FileCollection import org.gradle.api.file.SourceDirectorySet import org.gradle.api.initialization.Settings +import org.gradle.api.internal.artifacts.GradleApiVersionProvider import org.gradle.api.internal.plugins.DefaultPluginManager import org.gradle.api.invocation.Gradle import org.gradle.api.model.ObjectFactory @@ -55,6 +56,7 @@ import org.gradle.kotlin.dsl.support.serviceOf import org.gradle.plugin.devel.GradlePluginDevelopmentExtension import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin import org.gradle.tooling.model.kotlin.dsl.KotlinDslModelsParameters +import org.gradle.util.GradleVersion import java.io.File import java.util.function.Consumer import javax.inject.Inject @@ -230,7 +232,7 @@ fun Project.enableScriptCompilationOf( val (generatePrecompiledScriptPluginAccessors, _) = codeGenerationTask( - "accessors", + "accessors${GradleApiVersionProvider.getGradleApiSourceVersion().orElse(GradleVersion.current().baseVersion.version)}", "generatePrecompiledScriptPluginAccessors", kotlinSourceDirectorySet ) { @@ -246,6 +248,9 @@ fun Project.enableScriptCompilationOf( .map(java.lang.Boolean::parseBoolean) .orElse(false) ) + gradleApiVersion.set(providers + .systemProperty(GradleApiVersionProvider.GRADLE_API_SOURCE_VERSION_PROPERTY) + .orElse(GradleVersion.current().version)) plugins = scriptPlugins } diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt index c2843b99dc841..4e579e0d5554d 100644 --- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt +++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt @@ -122,6 +122,9 @@ abstract class GeneratePrecompiledScriptPluginAccessors @Inject internal constru @get:Input abstract val strict: Property + @get:Input + abstract val gradleApiVersion: Property + init { outputs.doNotCacheIf( "Generated accessors can only be cached in strict mode." diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragment.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragment.kt index 586d2c113dc9f..1eac1309fcc28 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragment.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragment.kt @@ -20,6 +20,7 @@ import kotlinx.metadata.jvm.JvmMethodSignature import kotlinx.metadata.jvm.KotlinClassMetadata import org.gradle.internal.classanalysis.AsmConstants.ASM_LEVEL +import org.gradle.util.GradleVersion import org.jetbrains.org.objectweb.asm.ClassVisitor import org.jetbrains.org.objectweb.asm.ClassWriter @@ -30,7 +31,8 @@ data class AccessorFragment( val source: String, val bytecode: BytecodeWriter, val metadata: MetadataWriter, - val signature: JvmMethodSignature + val signature: JvmMethodSignature, + val supportedSince: GradleVersion? = null ) diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragments.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragments.kt index ee39ed45b3cbf..54d10ca02417a 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragments.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorFragments.kt @@ -19,6 +19,7 @@ package org.gradle.kotlin.dsl.accessors import kotlinx.metadata.KmVariance import kotlinx.metadata.jvm.JvmMethodSignature import kotlinx.metadata.jvm.KotlinClassMetadata +import org.gradle.api.internal.artifacts.GradleApiVersionProvider import org.gradle.api.reflect.TypeOf import org.gradle.internal.deprecation.ConfigurationDeprecationType import org.gradle.internal.hash.Hashing.hashString @@ -49,6 +50,7 @@ import org.gradle.kotlin.dsl.support.bytecode.visitSignature import org.gradle.kotlin.dsl.support.bytecode.with import org.gradle.kotlin.dsl.support.bytecode.writeFunctionOf import org.gradle.kotlin.dsl.support.bytecode.writePropertyOf +import org.gradle.util.GradleVersion import org.jetbrains.org.objectweb.asm.MethodVisitor @@ -71,6 +73,9 @@ fun fragmentsForConfiguration(accessor: Accessor.ForConfiguration): Fragments = val (functionFlags, deprecationBlock) = if (config.hasDeclarationDeprecations()) publicFunctionWithAnnotationsFlags to config.getDeclarationDeprecationBlock() else publicFunctionFlags to "" + val sourceApiGradleVersion = GradleVersion.version( + GradleApiVersionProvider.getGradleApiSourceVersion().orElse(GradleVersion.current().version) + ).baseVersion className to sequenceOf( AccessorFragment( @@ -265,7 +270,8 @@ fun fragmentsForConfiguration(accessor: Accessor.ForConfiguration): Fragments = signature = JvmMethodSignature( propertyName, "(Lorg/gradle/api/artifacts/dsl/DependencyHandler;Lorg/gradle/api/provider/ProviderConvertible;Lorg/gradle/api/Action;)V" - ) + ), + supportedSince = GradleVersion.version("7.4") ), AccessorFragment( source = name.run { @@ -598,7 +604,7 @@ fun fragmentsForConfiguration(accessor: Accessor.ForConfiguration): Fragments = "" ) ) - ) + ).filter { it.supportedSince?.let { it <= sourceApiGradleVersion } ?: true } } diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorsClassPath.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorsClassPath.kt index e65978c4bf397..5455166d3818d 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorsClassPath.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/AccessorsClassPath.kt @@ -17,6 +17,7 @@ package org.gradle.kotlin.dsl.accessors import org.gradle.api.Project +import org.gradle.api.internal.artifacts.GradleApiVersionProvider import org.gradle.api.internal.file.FileCollectionFactory import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.tasks.ClasspathNormalizer @@ -45,6 +46,7 @@ import org.gradle.kotlin.dsl.concurrent.withAsynchronousIO import org.gradle.kotlin.dsl.support.ClassBytesRepository import org.gradle.kotlin.dsl.support.appendReproducibleNewLine import org.gradle.kotlin.dsl.support.useToRun +import org.gradle.util.GradleVersion import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf.Visibility import org.jetbrains.kotlin.metadata.deserialization.Flags @@ -118,6 +120,7 @@ class GenerateProjectAccessors( const val CLASSPATH_INPUT_PROPERTY = "classpath" const val SOURCES_OUTPUT_PROPERTY = "sources" const val CLASSES_OUTPUT_PROPERTY = "classes" + const val SOURCE_GRADLE_API_VERSION = "sourceGradleApiVersion" } override fun execute(executionRequest: UnitOfWork.ExecutionRequest): UnitOfWork.WorkOutput { @@ -145,6 +148,7 @@ class GenerateProjectAccessors( override fun identify(identityInputs: Map, identityFileInputs: Map): UnitOfWork.Identity { val hasher = Hashing.newHasher() requireNotNull(identityInputs[PROJECT_SCHEMA_INPUT_PROPERTY]).appendToHasher(hasher) + requireNotNull(identityInputs[SOURCE_GRADLE_API_VERSION]).appendToHasher(hasher) hasher.putHash(requireNotNull(identityFileInputs[CLASSPATH_INPUT_PROPERTY]).hash) val identityHash = hasher.hash().toString() return UnitOfWork.Identity { identityHash } @@ -158,6 +162,7 @@ class GenerateProjectAccessors( override fun visitIdentityInputs(visitor: InputVisitor) { visitor.visitInputProperty(PROJECT_SCHEMA_INPUT_PROPERTY) { hashCodeFor(projectSchema) } + visitor.visitInputProperty(SOURCE_GRADLE_API_VERSION) { GradleApiVersionProvider.getGradleApiSourceVersion().orElse(GradleVersion.current().version) } visitor.visitInputFileProperty( CLASSPATH_INPUT_PROPERTY, NON_INCREMENTAL, @@ -601,6 +606,10 @@ fun IO.writeAccessorsTo( } } +val sourceGradleApiVersion + get() = GradleApiVersionProvider.getGradleApiSourceVersion().orElse(GradleVersion.current().version) +val supportsProviderConvertible + get() = GradleVersion.version(sourceGradleApiVersion).baseVersion >= GradleVersion.version("7.4") internal fun fileHeaderWithImportsFor(accessorsPackage: String = kotlinDslPackageName) = """ @@ -623,7 +632,7 @@ import org.gradle.api.artifacts.dsl.ArtifactHandler import org.gradle.api.artifacts.dsl.DependencyConstraintHandler import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.provider.Provider -import org.gradle.api.provider.ProviderConvertible +${if (supportsProviderConvertible) "import org.gradle.api.provider.ProviderConvertible" else ""} import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/tasks/PrintAccessors.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/tasks/PrintAccessors.kt index 42e6d33208d15..eed45f169705b 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/tasks/PrintAccessors.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/tasks/PrintAccessors.kt @@ -18,6 +18,10 @@ package org.gradle.kotlin.dsl.accessors.tasks import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.internal.artifacts.GradleApiVersionProvider +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.internal.serialization.Cached @@ -43,6 +47,14 @@ abstract class PrintAccessors : DefaultTask() { protected abstract val projectSchemaProvider: ProjectSchemaProvider + @get:Input + @get:Optional + abstract val sourceGradleApiVersion: Property + + init { + sourceGradleApiVersion.set(GradleApiVersionProvider.getGradleApiSourceVersion().orElse(null)) + } + private val schema = Cached.of { schemaOf(project) } diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt index e4e0bbcca0186..4c96122b0de2b 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt @@ -17,12 +17,17 @@ package org.gradle.kotlin.dsl.provider import org.gradle.api.internal.ClassPathRegistry +import org.gradle.api.internal.artifacts.DependencyManagementServices +import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory +import org.gradle.api.internal.artifacts.dsl.dependencies.UnknownProjectFinder import org.gradle.api.internal.cache.StringInterner import org.gradle.api.internal.changedetection.state.ResourceSnapshotterCacheService import org.gradle.api.internal.classpath.ModuleRegistry import org.gradle.api.internal.file.FileCollectionFactory +import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.temp.TemporaryFileProvider +import org.gradle.api.internal.initialization.RootScriptDomainObjectContext import org.gradle.api.internal.initialization.loadercache.DefaultClasspathHasher import org.gradle.cache.internal.GeneratedGradleJarCache import org.gradle.groovy.scripts.internal.ScriptSourceHasher @@ -57,6 +62,10 @@ object BuildServices { classPathRegistry: ClassPathRegistry, classLoaderScopeRegistry: ClassLoaderScopeRegistry, dependencyFactory: DependencyFactory, + dependencyManagementServices: DependencyManagementServices, + fileResolver: FileResolver, + fileCollectionFactory: FileCollectionFactory, + dependencyMetaDataProvider: DependencyMetaDataProvider, jarCache: GeneratedGradleJarCache, temporaryFileProvider: TemporaryFileProvider, progressLoggerFactory: ProgressLoggerFactory @@ -66,7 +75,13 @@ object BuildServices { moduleRegistry, classPathRegistry, classLoaderScopeRegistry.coreAndPluginsScope, - gradleApiJarsProviderFor(dependencyFactory), + gradleApiJarsProviderFor(dependencyManagementServices.create( + fileResolver, + fileCollectionFactory, + dependencyMetaDataProvider, + UnknownProjectFinder("Some unknown project"), + RootScriptDomainObjectContext.PLUGINS + )), versionedJarCacheFor(jarCache), temporaryFileProvider, StandardJarGenerationProgressMonitorProvider(progressLoggerFactory) diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptClassPathProvider.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptClassPathProvider.kt index 3930326540098..9f81a9b61e13c 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptClassPathProvider.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptClassPathProvider.kt @@ -16,40 +16,32 @@ package org.gradle.kotlin.dsl.provider +import com.google.common.annotations.VisibleForTesting import org.gradle.api.Project - -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.SelfResolvingDependency import org.gradle.api.file.FileCollection - import org.gradle.api.internal.ClassPathRegistry +import org.gradle.api.internal.artifacts.DependencyResolutionServices +import org.gradle.api.internal.artifacts.GradleApiVersionProvider import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory import org.gradle.api.internal.classpath.ModuleRegistry import org.gradle.api.internal.file.FileCollectionFactory +import org.gradle.api.internal.file.temp.TemporaryFileProvider import org.gradle.api.internal.initialization.ClassLoaderScope - import org.gradle.internal.classloader.ClassLoaderVisitor import org.gradle.internal.classpath.ClassPath import org.gradle.internal.classpath.DefaultClassPath - +import org.gradle.kotlin.dsl.* import org.gradle.kotlin.dsl.codegen.generateApiExtensionsJar +import org.gradle.kotlin.dsl.support.ProgressMonitor import org.gradle.kotlin.dsl.support.gradleApiMetadataModuleName import org.gradle.kotlin.dsl.support.isGradleKotlinDslJar import org.gradle.kotlin.dsl.support.isGradleKotlinDslJarName -import org.gradle.kotlin.dsl.support.ProgressMonitor import org.gradle.kotlin.dsl.support.serviceOf - import org.gradle.util.internal.GFileUtils.moveFile - -import com.google.common.annotations.VisibleForTesting -import org.gradle.api.internal.file.temp.TemporaryFileProvider - import java.io.File - import java.net.URI import java.net.URISyntaxException import java.net.URL - import java.util.concurrent.ConcurrentHashMap @@ -213,13 +205,10 @@ class KotlinScriptClassPathProvider( internal -fun gradleApiJarsProviderFor(dependencyFactory: DependencyFactory): JarsProvider = - { (dependencyFactory.gradleApi() as SelfResolvingDependency).resolve() } - - -private -fun DependencyFactory.gradleApi(): Dependency = - createDependency(gradleApiNotation) +fun gradleApiJarsProviderFor(dependencyResolutionServices: DependencyResolutionServices): JarsProvider = + { + GradleApiVersionProvider.resolveGradleSourceApi(dependencyResolutionServices) + } private diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/DependencyHandlerDelegate.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/DependencyHandlerDelegate.kt index 298413c7ada34..753b9467f3c20 100644 --- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/DependencyHandlerDelegate.kt +++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/DependencyHandlerDelegate.kt @@ -87,6 +87,9 @@ abstract class DependencyHandlerDelegate : DependencyHandler { override fun gradleApi(): Dependency = delegate.gradleApi() + override fun gradleApi(version: String): Dependency = + delegate.gradleApi(version) + override fun gradleTestKit(): Dependency = delegate.gradleTestKit() diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java index a4308dd74c513..334eb6ecc97ca 100644 --- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java +++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java @@ -29,6 +29,7 @@ import org.gradle.api.file.CopySpec; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileCopyDetails; +import org.gradle.api.internal.artifacts.GradleApiVersionProvider; import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory; import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry; import org.gradle.api.internal.plugins.PluginDescriptor; @@ -152,7 +153,8 @@ private void registerPlugins(Project project, GradlePluginDevelopmentExtension e private void applyDependencies(Project project) { DependencyHandler dependencies = project.getDependencies(); - dependencies.add(API_CONFIGURATION, dependencies.gradleApi()); + GradleApiVersionProvider.addToConfiguration(project.getConfigurations().getByName(API_CONFIGURATION), dependencies); + GradleApiVersionProvider.addGradleSourceApiRepository(project.getRepositories()); } private void configureJarTask(Project project, GradlePluginDevelopmentExtension extension) { diff --git a/subprojects/plugins/src/main/java/org/gradle/initialization/buildsrc/GroovyBuildSrcProjectConfigurationAction.java b/subprojects/plugins/src/main/java/org/gradle/initialization/buildsrc/GroovyBuildSrcProjectConfigurationAction.java index c095c3c25498e..23babaf39ee61 100644 --- a/subprojects/plugins/src/main/java/org/gradle/initialization/buildsrc/GroovyBuildSrcProjectConfigurationAction.java +++ b/subprojects/plugins/src/main/java/org/gradle/initialization/buildsrc/GroovyBuildSrcProjectConfigurationAction.java @@ -17,6 +17,7 @@ package org.gradle.initialization.buildsrc; import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.internal.artifacts.GradleApiVersionProvider; import org.gradle.api.internal.project.ProjectInternal; import org.gradle.api.plugins.GroovyPlugin; import org.gradle.api.plugins.JavaLibraryPlugin; @@ -30,7 +31,8 @@ public void execute(ProjectInternal project) { project.getPluginManager().apply(GroovyPlugin.class); DependencyHandler dependencies = project.getDependencies(); - dependencies.add(JavaPlugin.API_CONFIGURATION_NAME, dependencies.gradleApi()); + GradleApiVersionProvider.addToConfiguration(project.getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME), dependencies); + GradleApiVersionProvider.addGradleSourceApiRepository(project.getRepositories()); dependencies.add(JavaPlugin.API_CONFIGURATION_NAME, dependencies.localGroovy()); } }