From 4566abbb2d3f45f35656dca5ed256e90b790ce8c Mon Sep 17 00:00:00 2001 From: Nikita Skvortsov Date: Fri, 4 Aug 2017 09:52:44 +0300 Subject: [PATCH] Rewirte Gradle's integration dependency resolver in java6 --- .../util/DependencyResolverImpl.groovy | 1080 -------------- .../tooling/util/DependencyResolverImpl.java | 1284 +++++++++++++++++ 2 files changed, 1284 insertions(+), 1080 deletions(-) delete mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.java diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy deleted file mode 100644 index 32ccf0ffc140..000000000000 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.groovy +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.jetbrains.plugins.gradle.tooling.util - -import com.google.common.collect.ArrayListMultimap -import com.google.common.collect.Lists -import com.google.common.collect.Multimap -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ModuleVersionIdentifier -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.artifacts.SelfResolvingDependency -import org.gradle.api.artifacts.component.ComponentIdentifier -import org.gradle.api.artifacts.component.ModuleComponentIdentifier -import org.gradle.api.artifacts.component.ModuleComponentSelector -import org.gradle.api.artifacts.component.ProjectComponentIdentifier -import org.gradle.api.artifacts.component.ProjectComponentSelector -import org.gradle.api.artifacts.result.* -import org.gradle.api.plugins.WarPlugin -import org.gradle.api.specs.Specs -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.SourceSetOutput -import org.gradle.api.tasks.bundling.AbstractArchiveTask -import org.gradle.api.tasks.compile.AbstractCompile -import org.gradle.language.base.artifact.SourcesArtifact -import org.gradle.language.java.artifact.JavadocArtifact -import org.gradle.plugins.ide.idea.IdeaPlugin -import org.gradle.util.GUtil -import org.gradle.util.GradleVersion -import org.jetbrains.annotations.NotNull -import org.jetbrains.annotations.Nullable -import org.jetbrains.plugins.gradle.model.* - -import java.util.regex.Matcher -import java.util.regex.Pattern - -/** - * @author Vladislav.Soroka - * @since 8/19/2015 - */ -class DependencyResolverImpl implements DependencyResolver { - - private static is4OrBetter = GradleVersion.current().baseVersion >= GradleVersion.version("4.0") - private static isJavaLibraryPluginSupported = is4OrBetter || - (GradleVersion.current() >= GradleVersion.version("3.4")) - private static isDependencySubstitutionsSupported = isJavaLibraryPluginSupported || - (GradleVersion.current() > GradleVersion.version("2.5")) - private static isArtifactResolutionQuerySupported = isDependencySubstitutionsSupported || - (GradleVersion.current() >= GradleVersion.version("2.0")) - - @NotNull - private final Project myProject - private final boolean myIsPreview - private final boolean myDownloadJavadoc - private final boolean myDownloadSources - private final SourceSetCachedFinder mySourceSetFinder - - @SuppressWarnings("GroovyUnusedDeclaration") - DependencyResolverImpl(@NotNull Project project, boolean isPreview) { - myProject = project - myIsPreview = isPreview - myDownloadJavadoc = false - myDownloadSources = false - mySourceSetFinder = new SourceSetCachedFinder(project) - } - - DependencyResolverImpl( - @NotNull Project project, - boolean isPreview, - boolean downloadJavadoc, - boolean downloadSources, - SourceSetCachedFinder sourceSetFinder) { - myProject = project - myIsPreview = isPreview - myDownloadJavadoc = downloadJavadoc - myDownloadSources = downloadSources - mySourceSetFinder = sourceSetFinder - } - - @Override - Collection resolveDependencies(@Nullable String configurationName) { - return resolveDependencies(configurationName, null) - } - - Collection resolveDependencies(@Nullable String configurationName, @Nullable String scope) { - if (configurationName == null) return Collections.emptyList() - def (result, resolvedFileDependencies) = resolveDependencies(myProject.configurations.findByName(configurationName), scope) - return result - } - - @Override - Collection resolveDependencies(@Nullable Configuration configuration) { - def (result, resolvedFileDependencies) = resolveDependencies(configuration, null) - return result - } - - def resolveDependencies(@Nullable Configuration configuration, @Nullable String scope) { - if (configuration == null) return [Collections.emptyList(), Collections.emptyList()] - if (configuration.allDependencies.isEmpty()) return [Collections.emptyList(), Collections.emptyList()] - - final Collection result = new LinkedHashSet<>() - - def resolvedFileDependencies = [] - if (!myIsPreview && isArtifactResolutionQuerySupported) { - def jvmLibrary = null - try { - jvmLibrary = Class.forName('org.gradle.jvm.JvmLibrary') - } - catch (ClassNotFoundException ignored) { - } - if (jvmLibrary == null) { - try { - jvmLibrary = Class.forName('org.gradle.runtime.jvm.JvmLibrary') - } - catch (ClassNotFoundException ignored) { - } - } - if (jvmLibrary != null) { - Class[] artifactTypes = ([myDownloadSources?SourcesArtifact:null, myDownloadJavadoc?JavadocArtifact:null] - null) as Class[] - Set resolvedArtifacts = configuration.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL) - - Multimap artifactMap = ArrayListMultimap.create() - resolvedArtifacts.each { artifactMap.put(it.moduleVersion.id, it) } - - def isBuildScriptConfiguration = myProject.buildscript.configurations.find { it == configuration } != null - //noinspection GroovyAssignabilityCheck - def dependencyHandler = isBuildScriptConfiguration ? myProject.buildscript.dependencies : myProject.dependencies - Set componentResults = dependencyHandler.createArtifactResolutionQuery() - .forComponents(resolvedArtifacts - .findAll { !isProjectDependencyArtifact(it) } - .collect { toComponentIdentifier(it.moduleVersion.id) }) - .withArtifacts(jvmLibrary, artifactTypes) - .execute() - .getResolvedComponents() - - Map componentResultsMap = [:] - componentResults.each { componentResultsMap.put(it.id, it) } - - Set processedConfigurations = new HashSet<>() - def projectDeps - projectDeps = { Configuration conf, map = ArrayListMultimap.create() -> - if(!processedConfigurations.add(conf)) return map - conf.incoming.dependencies.findAll { it instanceof ProjectDependency }.each { it -> - map.put(toComponentIdentifier(it.group, it.name, it.version), it as ProjectDependency) - projectDeps(getTargetConfiguration(it as ProjectDependency), map) - } - map - } - Multimap configurationProjectDependencies = projectDeps(configuration) - - ResolutionResult resolutionResult = configuration.incoming.resolutionResult - if(!configuration.resolvedConfiguration.hasError()) { - Collection fileDeps = new LinkedHashSet(configuration.incoming.files.files) - artifactMap.values().each { - fileDeps.remove(it.file) - } - configurationProjectDependencies.values().each { - def intersect = fileDeps.intersect(it.resolve()) - if(!intersect.isEmpty()) { - def fileCollectionDependency = new DefaultFileCollectionDependency(intersect) - fileCollectionDependency.scope = scope - result.add(fileCollectionDependency) - fileDeps.removeAll(intersect) - } - } - fileDeps.each { - def fileCollectionDependency = new DefaultFileCollectionDependency([it]) - fileCollectionDependency.scope = scope - result.add(fileCollectionDependency) - } - } - - def dependencyResultsTransformer = new DependencyResultsTransformer(artifactMap, componentResultsMap, configurationProjectDependencies, scope) - result.addAll(dependencyResultsTransformer.transform(resolutionResult.root.dependencies)) - - resolvedFileDependencies.addAll(dependencyResultsTransformer.resolvedDepsFiles) - } - } - - if (myIsPreview || !isArtifactResolutionQuerySupported) { - def projectDependencies = findDependencies(configuration, configuration.allDependencies, scope) - result.addAll(projectDependencies) - } - def fileDependencies = findAllFileDependencies(configuration.allDependencies, scope) - result.addAll(fileDependencies - resolvedFileDependencies) - - return [new ArrayList(result), resolvedFileDependencies] - } - - @Override - Collection resolveDependencies(@NotNull SourceSet sourceSet) { - Collection result = new ArrayList<>() - - // resolve compile dependencies - def isMainSourceSet = sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME - String deprecatedCompileConfigurationName = isMainSourceSet ? "compile" : GUtil.toCamelCase(sourceSet.name) + "Compile" - def deprecatedCompileConfiguration = myProject.configurations.findByName(deprecatedCompileConfigurationName) - def compileConfigurationName = sourceSet.compileConfigurationName - def compileClasspathConfiguration = myProject.configurations.findByName(compileConfigurationName + 'Classpath') - def originCompileConfiguration = myProject.configurations.findByName(compileConfigurationName) - def compileConfiguration = compileClasspathConfiguration ?: originCompileConfiguration - def compileOnlyConfiguration = isJavaLibraryPluginSupported ? myProject.configurations.findByName(sourceSet.compileOnlyConfigurationName) : null - - def compileScope = 'COMPILE' - def (compileDependencies, resolvedCompileFileDependencies) = resolveDependencies(compileConfiguration, compileScope) - // resolve runtime dependencies - def runtimeConfigurationName = sourceSet.runtimeConfigurationName - def runtimeClasspathConfiguration = myProject.configurations.findByName(runtimeConfigurationName + 'Classpath') - def originRuntimeConfiguration = myProject.configurations.findByName(runtimeConfigurationName) - def runtimeConfiguration = runtimeClasspathConfiguration ?: originRuntimeConfiguration - - def runtimeScope = 'RUNTIME' - def (runtimeDependencies, resolvedRuntimeFileDependencies) = resolveDependencies(runtimeConfiguration, runtimeScope) - - def providedScope = 'PROVIDED' - - Multimap resolvedMap = ArrayListMultimap.create() - - boolean checkCompileOnlyDeps = compileClasspathConfiguration && !originCompileConfiguration.resolvedConfiguration.hasError() - new DependencyTraverser(compileDependencies).each { - def resolvedObj = resolve(it) - resolvedMap.put(resolvedObj, it) - - // since version 3.4 compileOnly no longer extends compile - // so, we can use compileOnly configuration for the check - Object[] resolvedObjArray = resolvedObj instanceof Collection ? ((Collection)resolvedObj).toArray() : [resolvedObj] - if (isJavaLibraryPluginSupported) { - if (compileOnlyConfiguration != null && compileOnlyConfiguration.containsAll(resolvedObjArray)) { - // deprecated 'compile' configuration still can be used - if (deprecatedCompileConfiguration == null || !deprecatedCompileConfiguration.containsAll(resolvedObjArray)) { - ((AbstractExternalDependency)it).scope = providedScope - } - } - } - else { - if (checkCompileOnlyDeps && !originCompileConfiguration.containsAll(resolvedObjArray) && - !runtimeConfiguration.containsAll(resolvedObjArray)) { - ((AbstractExternalDependency)it).scope = providedScope - } - } - } - - Multimap resolvedRuntimeMap = ArrayListMultimap.create() - new DependencyTraverser(runtimeDependencies).each { - Collection dependencies = resolvedMap.get(resolve(it)) - if (dependencies && !dependencies.isEmpty() && it.dependencies.isEmpty()) { - runtimeDependencies.remove(it) - ((AbstractExternalDependency)it).scope = compileScope - dependencies.each {((AbstractExternalDependency)it).scope = compileScope} - } - else { - if(dependencies) { - ((AbstractExternalDependency)it).scope = compileScope - } - resolvedRuntimeMap.put(resolve(it), it) - } - } - resolvedMap.putAll(resolvedRuntimeMap) - - result.addAll(compileDependencies) - result.addAll(runtimeDependencies) - result.unique() - - // merge file dependencies - def jvmLanguages = ['Java', 'Groovy', 'Scala'] - def sourceSetCompileTaskPrefix = sourceSet.name == 'main' ? '' : sourceSet.name - def compileTasks = jvmLanguages.collect { 'compile' + sourceSetCompileTaskPrefix.capitalize() + it } - - Map compileClasspathOrder = new LinkedHashMap() - Set compileClasspathFiles = new LinkedHashSet<>() - - compileTasks.each { - def compileTask = myProject.tasks.findByName(it) - if (compileTask instanceof AbstractCompile) { - try { - def files = new ArrayList<>(compileTask.classpath.files) - files.removeAll(compileClasspathFiles) - compileClasspathFiles.addAll(files) - } - catch (ignore) { - } - } - } - - try { - compileClasspathFiles = compileClasspathFiles.isEmpty() ? sourceSet.compileClasspath.files : compileClasspathFiles - } - catch (ignore) { - } - int order = 0 - for (File file : compileClasspathFiles) { - compileClasspathOrder.put(file, order++) - } - Map runtimeClasspathOrder = new LinkedHashMap() - order = 0 - Set runtimeClasspathFiles = new LinkedHashSet() - try { - def files = sourceSet.runtimeClasspath.files - for (File file : files) { - runtimeClasspathOrder.put(file, order++) - } - runtimeClasspathFiles.addAll(files) - } - catch (ignore) { - } - - runtimeClasspathFiles -= compileClasspathFiles - runtimeClasspathFiles -= sourceSet.output.files - compileClasspathFiles -= sourceSet.output.files - - Multimap resolvedDependenciesMap = ArrayListMultimap.create() - resolvedDependenciesMap.putAll(compileScope, resolvedCompileFileDependencies) - resolvedDependenciesMap.putAll(runtimeScope, resolvedRuntimeFileDependencies) - Project rootProject = myProject.rootProject - - new DependencyTraverser(result).each { - def dependency = it - def scope = dependency.scope - order = -1 - if (dependency instanceof ExternalProjectDependency) { - ExternalProjectDependency projectDependency = dependency - def project = rootProject.findProject(projectDependency.projectPath) - def configuration = project?.configurations?.findByName(projectDependency.configurationName) - configuration?.allArtifacts?.files?.files?.each { - resolvedDependenciesMap.put(scope, it) - def classpathOrderMap = scope == compileScope ? compileClasspathOrder : - scope == runtimeScope ? runtimeClasspathOrder : null - if (classpathOrderMap) { - def fileOrder = classpathOrderMap.get(it) - if (fileOrder != null && (order == -1 || fileOrder < order)) { - order = fileOrder - } - } - } - - //noinspection GrUnresolvedAccess - if (project.hasProperty("sourceSets") && (project.sourceSets instanceof SourceSetContainer) && project.sourceSets.main) { - //noinspection GrUnresolvedAccess - addSourceSetOutputDirsAsSingleEntryLibraries(result, project.sourceSets.main, runtimeClasspathOrder, scope) - } - } - else if (dependency instanceof ExternalLibraryDependency) { - resolvedDependenciesMap.put(scope, dependency.file) - def classpathOrderMap = scope == compileScope ? compileClasspathOrder : - scope == runtimeScope ? runtimeClasspathOrder : null - if (classpathOrderMap) { - def fileOrder = classpathOrderMap.get(dependency.file) - order = fileOrder != null ? fileOrder : -1 - } - } - else if (dependency instanceof FileCollectionDependency) { - for (File file : dependency.files) { - resolvedDependenciesMap.put(scope, file) - def classpathOrderMap = scope == compileScope ? compileClasspathOrder : - scope == runtimeScope ? runtimeClasspathOrder : null - if (classpathOrderMap) { - def fileOrder = classpathOrderMap.get(file) - if (fileOrder != null && (order == -1 || fileOrder < order)) { - order = fileOrder - } - if (order == 0) break - } - } - } - - if (dependency instanceof AbstractExternalDependency) { - dependency.classpathOrder = order - } - } - - compileClasspathFiles.removeAll(resolvedDependenciesMap.get(compileScope)) - compileClasspathFiles.removeAll(resolvedDependenciesMap.get(providedScope)) - runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(runtimeScope)) - runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(compileScope)) - runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(providedScope)) - - Collection fileDependencies = new ArrayList<>() - mapFileDependencies(runtimeClasspathFiles, runtimeScope, fileDependencies) - mapFileDependencies(compileClasspathFiles, compileScope, fileDependencies) - - fileDependencies.each { - def dependency = it - def scope = dependency.scope - order = -1 - if (dependency instanceof ExternalLibraryDependency) { - def classpathOrderMap = scope == compileScope ? compileClasspathOrder : - scope == runtimeScope ? runtimeClasspathOrder : null - if (classpathOrderMap) { - def fileOrder = classpathOrderMap.get(dependency.file) - order = fileOrder != null ? fileOrder : -1 - } - } - if (dependency instanceof AbstractExternalDependency) { - dependency.classpathOrder = order - } - } - result.addAll(fileDependencies) - - if (!compileClasspathFiles.isEmpty()) { - final compileClasspathFilesDependency = new DefaultFileCollectionDependency(compileClasspathFiles) - compileClasspathFilesDependency.scope = compileScope - - order = -1 - for (File file : compileClasspathFiles) { - def fileOrder = compileClasspathOrder.get(file) - if (fileOrder != null && (order == -1 || fileOrder < order)) { - order = fileOrder - } - if (order == 0) break - } - - if (order != -1) { - compileClasspathFilesDependency.classpathOrder = order - } - result.add(compileClasspathFilesDependency) - for (File file : compileClasspathFiles) { - def outputDirSourceSet = mySourceSetFinder.findByArtifact(file.path) - if(outputDirSourceSet) { - addSourceSetOutputDirsAsSingleEntryLibraries(result, outputDirSourceSet, compileClasspathOrder, compileScope) - } - } - } - - if (!runtimeClasspathFiles.isEmpty()) { - final runtimeClasspathFilesDependency = new DefaultFileCollectionDependency(runtimeClasspathFiles) - runtimeClasspathFilesDependency.scope = runtimeScope - - order = -1 - for (File file : runtimeClasspathFiles) { - def fileOrder = runtimeClasspathOrder.get(file) - if (fileOrder != null && (order == -1 || fileOrder < order)) { - order = fileOrder - } - if (order == 0) break - } - - runtimeClasspathFilesDependency.classpathOrder = order - result.add(runtimeClasspathFilesDependency) - - for (File file : runtimeClasspathFiles) { - def outputDirSourceSet = mySourceSetFinder.findByArtifact(file.path) - if(outputDirSourceSet) { - addSourceSetOutputDirsAsSingleEntryLibraries(result, outputDirSourceSet, runtimeClasspathOrder, runtimeScope) - } - } - } - - addSourceSetOutputDirsAsSingleEntryLibraries(result, sourceSet, runtimeClasspathOrder, runtimeScope) - - // handle provided dependencies - def providedConfigurations = new LinkedHashSet() - resolvedMap = ArrayListMultimap.create() - new DependencyTraverser(result).each { resolvedMap.put(resolve(it), it) } - - if (sourceSet.name == 'main' && myProject.plugins.findPlugin(WarPlugin)) { - providedConfigurations.add(myProject.configurations.findByName('providedCompile')) - providedConfigurations.add(myProject.configurations.findByName('providedRuntime')) - } - - final IdeaPlugin ideaPlugin = myProject.getPlugins().findPlugin(IdeaPlugin.class) - if (ideaPlugin) { - def scopes = ideaPlugin.model.module.scopes - def providedPlusScopes = scopes.get(providedScope) - if (providedPlusScopes && providedPlusScopes.get("plus")) { - // filter default 'compileClasspath' for slight optimization since it has been already processed as compile dependencies - def ideaPluginProvidedConfigurations = providedPlusScopes.get("plus").findAll { it.name != "compileClasspath"} - // since gradle 3.4 'idea' plugin PROVIDED scope.plus contains 'providedCompile' and 'providedRuntime' configurations - // see https://github.com/gradle/gradle/commit/c46897ae840c5ebb32946009c83d861ee194ab96#diff-0fa13ec419e839ef2d355b7feb88b815R432 - ideaPluginProvidedConfigurations.removeAll(providedConfigurations) - ideaPluginProvidedConfigurations.each { - def (providedDependencies, _) = resolveDependencies(it, providedScope) - new DependencyTraverser(providedDependencies).each { resolvedMap.put(resolve(it), it) } - result.addAll(providedDependencies) - } - } - } - providedConfigurations.each { - def (providedDependencies, _) = resolveDependencies(it, providedScope) - new DependencyTraverser(providedDependencies).each { - Collection dependencies = resolvedMap.get(resolve(it)) - if (!dependencies.isEmpty()) { - if (it.dependencies.isEmpty()) { - providedDependencies.remove(it) - } - dependencies.each { - ((AbstractExternalDependency)it).scope = providedScope - } - } - else { - resolvedMap.put(resolve(it), it) - } - } - result.addAll(providedDependencies) - } - - return removeDuplicates(resolvedMap, result) - } - - private static List removeDuplicates( - ArrayListMultimap resolvedMap, List result) { - resolvedMap.asMap().values().each { - def toRemove = [] - def isCompileScope = false - def isProvidedScope = false - it.each { - if (it.dependencies.isEmpty()) { - toRemove.add(it) - if(it.scope == 'COMPILE') isCompileScope = true - else if(it.scope == 'PROVIDED') isProvidedScope = true - } - } - if (toRemove.size() != it.size()) { - result.removeAll(toRemove) - } - else if (toRemove.size() > 1) { - toRemove = toRemove.drop(1) - result.removeAll(toRemove) - } - if(!toRemove.isEmpty()) { - def retained = it - toRemove - if(!retained.isEmpty()) { - def retainedDependency = retained.find{true} as AbstractExternalDependency - if(retainedDependency instanceof AbstractExternalDependency && retainedDependency.scope != 'COMPILE') { - if(isCompileScope) retainedDependency.scope = 'COMPILE' - else if(isProvidedScope) retainedDependency.scope = 'PROVIDED' - } - } - } - } - - return result.unique() - } - - static def resolve(ExternalDependency dependency) { - if (dependency instanceof ExternalLibraryDependency) { - return dependency.file - } else if (dependency instanceof FileCollectionDependency) { - return dependency.files - } else if (dependency instanceof ExternalMultiLibraryDependency) { - return dependency.files - } else if (dependency instanceof ExternalProjectDependency) { - return dependency.projectDependencyArtifacts - } - null - } - - private static void addSourceSetOutputDirsAsSingleEntryLibraries( - Collection dependencies, - SourceSet sourceSet, - Map classpathOrder, - String scope) { - Set runtimeOutputDirs = sourceSet.output.dirs.files - runtimeOutputDirs.each { - final runtimeOutputDirsDependency = new DefaultFileCollectionDependency([it]) - runtimeOutputDirsDependency.scope = scope - def fileOrder = classpathOrder.get(it) - runtimeOutputDirsDependency.classpathOrder = fileOrder != null ? fileOrder : -1 - dependencies.add(runtimeOutputDirsDependency) - } - } - - - @Nullable - ExternalLibraryDependency resolveLibraryByPath(File file, String scope) { - File modules2Dir = new File(myProject.gradle.gradleUserHomeDir, "caches/modules-2/files-2.1") - return resolveLibraryByPath(file, modules2Dir, scope) - } - - @Nullable - static ExternalLibraryDependency resolveLibraryByPath(File file, File modules2Dir, String scope) { - File sourcesFile = null - def modules2Path = modules2Dir.canonicalPath - def filePath = file.canonicalPath - if (filePath.startsWith(modules2Path)) { - List parents = new ArrayList<>() - File parent = file.parentFile - while(parent && !parent.name.equals(modules2Dir.name)) { - parents.add(parent) - parent = parent.parentFile - } - - def groupDir = parents.get(parents.size() - 1) - def artifactDir = parents.get(parents.size() - 2) - def versionDir = parents.get(parents.size() - 3) - - def parentFile = versionDir - if (parentFile != null) { - def hashDirs = parentFile.listFiles() - if (hashDirs != null) { - for (File hashDir : hashDirs) { - def sourcesJars = hashDir.listFiles(new FilenameFilter() { - @Override - boolean accept(File dir, String name) { - return name.endsWith("sources.jar") - } - }) - - if (sourcesJars != null && sourcesJars.length > 0) { - sourcesFile = sourcesJars[0] - break - } - } - - def packaging = resolvePackagingType(file) - def classifier = resolveClassifier(artifactDir.name, versionDir.name, file) - return new DefaultExternalLibraryDependency( - name: artifactDir.name, - group: groupDir.name, - packaging: packaging, - classifier: classifier, - version: versionDir.name, - file: file, - source: sourcesFile, - scope: scope - ) - } - } - } - - null - } - - def mapFileDependencies(Set fileDependencies, String scope, Collection dependencies) { - File modules2Dir = new File(myProject.gradle.gradleUserHomeDir, "caches/modules-2/files-2.1") - List toRemove = new ArrayList() - for (File file : fileDependencies) { - def libraryDependency = resolveLibraryByPath(file, modules2Dir, scope) - if (libraryDependency) { - dependencies.add(libraryDependency) - toRemove.add(file) - } - else { - //noinspection GrUnresolvedAccess - def name = file.name.lastIndexOf('.').with { it != -1 ? file.name[0.. findAllFileDependencies( - Collection dependencies, String scope) { - Set result = new LinkedHashSet<>() - - dependencies.each { - try { - if (it instanceof SelfResolvingDependency && !(it instanceof ProjectDependency)) { - def files = it.resolve() - if (files && !files.isEmpty()) { - final dependency = new DefaultFileCollectionDependency(files) - dependency.scope = scope - result.add(dependency) - } - } - } - catch (ignore) { - } - } - - return result - } - - private Set findDependencies( - Configuration configuration, - Collection dependencies, - String scope) { - Set result = new LinkedHashSet<>() - - Set resolvedArtifacts = myIsPreview ? new HashSet<>() : - configuration.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL) - - Multimap artifactMap = ArrayListMultimap.create() - resolvedArtifacts.each { artifactMap.put(toMyModuleIdentifier(it.moduleVersion.id), it) } - - dependencies.each { - try { - if (it instanceof ProjectDependency) { - def project = it.getDependencyProject() - Configuration targetConfiguration = getTargetConfiguration(it) - - final projectDependency = new DefaultExternalProjectDependency( - name: project.name, - group: project.group, - version: project.version, - scope: scope, - projectPath: project.path, - configurationName: targetConfiguration.name - ) - projectDependency.projectDependencyArtifacts = targetConfiguration.allArtifacts.files.files - result.add(projectDependency) - } - else if (it instanceof Dependency) { - def artifactsResult = artifactMap.get(toMyModuleIdentifier(it.name, it.group)) - if (artifactsResult && !artifactsResult.isEmpty()) { - def artifact = artifactsResult.find{true} - def packaging = artifact.extension ?: 'jar' - def classifier = artifact.classifier - File sourcesFile = resolveLibraryByPath(artifact.file, scope)?.source - def libraryDependency = new DefaultExternalLibraryDependency( - name: it.name, - group: it.group, - packaging: packaging, - classifier: classifier, - version: artifact.moduleVersion.id.version, - scope: scope, - file: artifact.file, - source: sourcesFile - ) - result.add(libraryDependency) - } - else { - if (!(it instanceof SelfResolvingDependency) && !myIsPreview) { - final dependency = new DefaultUnresolvedExternalDependency( - name: it.name, - group: it.group, - version: it.version, - scope: scope, - failureMessage: "Could not find " + it.group + ":" + it.name + ":" + it.version - ) - result.add(dependency) - } - } - } - } - catch (ignore) { - } - } - - return result - } - - private static Configuration getTargetConfiguration(ProjectDependency projectDependency) { - return !is4OrBetter ? projectDependency.projectConfiguration : - projectDependency.dependencyProject.configurations.getByName(projectDependency.targetConfiguration ?: 'default') - } - - class DependencyResultsTransformer { - Collection handledDependencyResults - Multimap artifactMap - Map componentResultsMap - Multimap configurationProjectDependencies - String scope - Set resolvedDepsFiles = [] - - DependencyResultsTransformer( - Multimap artifactMap, - Map componentResultsMap, - Multimap configurationProjectDependencies, - String scope) { - this.handledDependencyResults = Lists.newArrayList() - this.artifactMap = artifactMap - this.componentResultsMap = componentResultsMap - this.configurationProjectDependencies = configurationProjectDependencies - this.scope = scope - } - - Set transform(Collection dependencyResults) { - - Set dependencies = new LinkedHashSet<>() - dependencyResults.each { DependencyResult dependencyResult -> - - // dependency cycles check - if (!handledDependencyResults.contains(dependencyResult)) { - handledDependencyResults.add(dependencyResult) - - if (dependencyResult instanceof ResolvedDependencyResult) { - def componentResult = dependencyResult.selected - def componentSelector = dependencyResult.requested - def componentIdentifier = toComponentIdentifier(componentResult.moduleVersion) - def name = componentResult.moduleVersion.name - def group = componentResult.moduleVersion.group - def version = componentResult.moduleVersion.version - def selectionReason = componentResult.selectionReason.description - def resolveFromArtifacts = componentSelector instanceof ModuleComponentSelector - if (componentSelector instanceof ProjectComponentSelector) { - def projectDependencies = configurationProjectDependencies.get(componentIdentifier) - Collection dependencyConfigurations - if(projectDependencies.isEmpty()) { - def dependencyProject = myProject.findProject(componentSelector.projectPath) - if(dependencyProject) { - def dependencyProjectConfiguration = dependencyProject.getConfigurations().getByName(Dependency.DEFAULT_CONFIGURATION) - dependencyConfigurations = [dependencyProjectConfiguration] - } else { - dependencyConfigurations = [] - resolveFromArtifacts = true - selectionReason = "composite build substitution" - } - } else { - dependencyConfigurations = projectDependencies.collect { getTargetConfiguration(it) } - } - - dependencyConfigurations.each { - if (it.name == Dependency.DEFAULT_CONFIGURATION) { - final dependency = new DefaultExternalProjectDependency( - name: name, - group: group, - version: version, - scope: scope, - selectionReason: selectionReason, - projectPath: (componentSelector as ProjectComponentSelector).projectPath, - configurationName: it.name - ) - dependency.projectDependencyArtifacts = it.allArtifacts.files.files - dependency.projectDependencyArtifacts.each { resolvedDepsFiles.add(it) } - if(it.artifacts.size() == 1) { - def publishArtifact = it.allArtifacts.find{true} - dependency.classifier = publishArtifact.classifier - dependency.packaging = publishArtifact.extension ?: 'jar' - } - - if (componentResult != dependencyResult.from) { - dependency.dependencies.addAll( - transform(componentResult.dependencies) - ) - } - dependencies.add(dependency) - } - else { - final dependency = new DefaultExternalProjectDependency( - name: name, - group: group, - version: version, - scope: scope, - selectionReason: selectionReason, - projectPath: (componentSelector as ProjectComponentSelector).projectPath, - configurationName: it.name - ) - dependency.projectDependencyArtifacts = it.allArtifacts.files.files - dependency.projectDependencyArtifacts.each { resolvedDepsFiles.add(it) } - if(it.artifacts.size() == 1) { - def publishArtifact = it.allArtifacts.find{true} - dependency.classifier = publishArtifact.classifier - dependency.packaging = publishArtifact.extension ?: 'jar' - } - - if (componentResult != dependencyResult.from) { - dependency.dependencies.addAll( - transform(componentResult.dependencies) - ) - } - dependencies.add(dependency) - - def files = [] - def artifacts = it.getArtifacts() - if (artifacts && !artifacts.isEmpty()) { - def artifact = artifacts.find{true} - if (artifact.hasProperty("archiveTask") && - (artifact.archiveTask instanceof org.gradle.api.tasks.bundling.AbstractArchiveTask)) { - def archiveTask = artifact.archiveTask as AbstractArchiveTask - resolvedDepsFiles.add(new File(archiveTask.destinationDir, archiveTask.archiveName)) - - def mainSpec = archiveTask.mainSpec - def sourcePaths - if (mainSpec.metaClass.respondsTo(mainSpec, 'getSourcePaths')) { - sourcePaths = mainSpec.getSourcePaths() - } - else if (mainSpec.hasProperty('sourcePaths')) { - sourcePaths = mainSpec.sourcePaths - } - if (sourcePaths) { - (sourcePaths.flatten() as List).each { path -> - if (path instanceof String) { - def file = new File(path) - if (file.isAbsolute()) { - files.add(file) - } - } - else if (path instanceof SourceSetOutput) { - files.addAll(path.files) - } - } - } - } - } - - if(!files.isEmpty()) { - final fileCollectionDependency = new DefaultFileCollectionDependency(files) - fileCollectionDependency.scope = scope - dependencies.add(fileCollectionDependency) - resolvedDepsFiles.addAll(files) - } - } - } - } - if (resolveFromArtifacts) { - def artifacts = artifactMap.get(componentResult.moduleVersion) - def artifact = artifacts?.find { true } - - if (artifacts?.isEmpty()) { - dependencies.addAll( - transform(componentResult.dependencies) - ) - } - boolean first = true - artifacts?.each { - artifact = it - def packaging = it.extension ?: 'jar' - def classifier = it.classifier - final dependency - if (isProjectDependencyArtifact(artifact)) { - def artifactComponentIdentifier = artifact.id.componentIdentifier as ProjectComponentIdentifier - dependency = new DefaultExternalProjectDependency( - name: name, - group: group, - version: version, - scope: scope, - selectionReason: selectionReason, - projectPath: artifactComponentIdentifier.projectPath, - configurationName: Dependency.DEFAULT_CONFIGURATION - ) - dependency.projectDependencyArtifacts = artifactMap.get(componentResult.moduleVersion).collect { it.file } - dependency.projectDependencyArtifacts.each { resolvedDepsFiles.add(it) } - } - else { - dependency = new DefaultExternalLibraryDependency( - name: name, - group: group, - packaging: packaging, - classifier: classifier, - version: version, - scope: scope, - selectionReason: selectionReason, - file: artifact.file - ) - - def artifactsResult = componentResultsMap.get(componentIdentifier) - if (artifactsResult) { - def sourcesResult = artifactsResult.getArtifacts(SourcesArtifact)?.find { it instanceof ResolvedArtifactResult } - if (sourcesResult) { - dependency.setSource(((ResolvedArtifactResult)sourcesResult).getFile()) - } - def javadocResult = artifactsResult.getArtifacts(JavadocArtifact)?.find { it instanceof ResolvedArtifactResult } - if (javadocResult) { - dependency.setJavadoc(((ResolvedArtifactResult)javadocResult).getFile()) - } - } - } - if (first) { - dependency.dependencies.addAll( - transform(componentResult.dependencies) - ) - first = false - } - - dependencies.add(dependency) - resolvedDepsFiles.add(artifact.file) - } - } - } - - if (dependencyResult instanceof UnresolvedDependencyResult) { - def componentResult = dependencyResult.attempted - if (componentResult instanceof ModuleComponentSelector) { - final dependency = new DefaultUnresolvedExternalDependency( - name: componentResult.module, - group: componentResult.group, - version: componentResult.version, - scope: scope, - failureMessage: dependencyResult.failure.message - ) - dependencies.add(dependency) - } - } - } - } - - return dependencies - } - } - - private static boolean isProjectDependencyArtifact(ResolvedArtifact artifact) { - return isDependencySubstitutionsSupported && artifact.id.componentIdentifier instanceof ProjectComponentIdentifier - } - - private static toMyModuleIdentifier(ModuleVersionIdentifier id) { - return new MyModuleIdentifier(name: id.getName(), group: id.getGroup()) - } - - private static toMyModuleIdentifier(String name, String group) { - return new MyModuleIdentifier(name: name, group: group) - } - - static class MyModuleIdentifier { - String name - String group - - boolean equals(o) { - if (this.is(o)) return true - if (!(o instanceof MyModuleIdentifier)) return false - - MyModuleIdentifier that = (MyModuleIdentifier)o - - if (group != that.group) return false - if (name != that.name) return false - - return true - } - - int hashCode() { - int result = (group != null ? group.hashCode() : 0) - result = 31 * result + (name != null ? name.hashCode() : 0) - return result - } - - @Override - String toString() { - return "$group:$name" - } - } -} diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.java new file mode 100644 index 000000000000..4397a8e72415 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/util/DependencyResolverImpl.java @@ -0,0 +1,1284 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.jetbrains.plugins.gradle.tooling.util; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import groovy.lang.MetaMethod; +import groovy.lang.MetaProperty; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.*; +import org.gradle.api.artifacts.component.*; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.artifacts.result.*; +import org.gradle.api.component.Artifact; +import org.gradle.api.component.Component; +import org.gradle.api.plugins.WarPlugin; +import org.gradle.api.specs.Specs; +import org.gradle.api.tasks.AbstractCopyTask; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.SourceSetOutput; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.api.tasks.compile.AbstractCompile; +import org.gradle.language.base.artifact.SourcesArtifact; +import org.gradle.language.java.artifact.JavadocArtifact; +import org.gradle.plugins.ide.idea.IdeaPlugin; +import org.gradle.util.GUtil; +import org.gradle.util.GradleVersion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.*; +import org.jetbrains.plugins.gradle.model.ExternalDependency; +import org.jetbrains.plugins.gradle.model.FileCollectionDependency; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.google.common.base.Predicates.isNull; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.find; +import static org.codehaus.groovy.runtime.StringGroovyMethods.capitalize; + +/** + * @author Vladislav.Soroka + * @since 8/19/2015 + */ +public class DependencyResolverImpl implements DependencyResolver { + + private static boolean is4OrBetter = GradleVersion.current().getBaseVersion().compareTo(GradleVersion.version("4.0")) >= 0; + private static boolean isJavaLibraryPluginSupported = is4OrBetter || + (GradleVersion.current().compareTo(GradleVersion.version("3.4")) >= 0); + private static boolean isDependencySubstitutionsSupported = isJavaLibraryPluginSupported || + (GradleVersion.current().compareTo(GradleVersion.version("2.5")) > 0); + private static boolean isArtifactResolutionQuerySupported = isDependencySubstitutionsSupported || + (GradleVersion.current().compareTo(GradleVersion.version("2.0")) >= 0); + + @NotNull + private final Project myProject; + private final boolean myIsPreview; + private final boolean myDownloadJavadoc; + private final boolean myDownloadSources; + private final SourceSetCachedFinder mySourceSetFinder; + + @SuppressWarnings("GroovyUnusedDeclaration") + public DependencyResolverImpl(@NotNull Project project, boolean isPreview) { + myProject = project; + myIsPreview = isPreview; + myDownloadJavadoc = false; + myDownloadSources = false; + mySourceSetFinder = new SourceSetCachedFinder(project); + } + + public DependencyResolverImpl( + @NotNull Project project, + boolean isPreview, + boolean downloadJavadoc, + boolean downloadSources, + SourceSetCachedFinder sourceSetFinder) { + myProject = project; + myIsPreview = isPreview; + myDownloadJavadoc = downloadJavadoc; + myDownloadSources = downloadSources; + mySourceSetFinder = sourceSetFinder; + } + + @Override + public Collection resolveDependencies(@Nullable String configurationName) { + return resolveDependencies(configurationName, null); + } + + Collection resolveDependencies(@Nullable String configurationName, @Nullable String scope) { + if (configurationName == null) return Collections.emptyList(); + return resolveDependencies(myProject.getConfigurations().findByName(configurationName), scope).getExternalDeps(); + } + + @Override + public Collection resolveDependencies(@Nullable Configuration configuration) { + return resolveDependencies(configuration, null).getExternalDeps(); + } + + private static class ExternalDepsResolutionResult { + public static ExternalDepsResolutionResult + EMPTY = new ExternalDepsResolutionResult(Collections.emptySet(), Collections.emptySet()); + private final Collection externalDeps; + private final Collection resolvedFiles; + + private ExternalDepsResolutionResult(Collection deps, Collection files) { + externalDeps = deps; + resolvedFiles = files; + } + + public Collection getResolvedFiles() { + return resolvedFiles; + } + + public Collection getExternalDeps() { + return externalDeps; + } + } + + ExternalDepsResolutionResult resolveDependencies(@Nullable Configuration configuration, @Nullable String scope) { + if (configuration == null) { + return ExternalDepsResolutionResult.EMPTY; + } + + if (configuration.getAllDependencies().isEmpty()) { + return ExternalDepsResolutionResult.EMPTY; + } + + final Collection result = new LinkedHashSet(); + + final List resolvedFileDependencies = new ArrayList(); + + if (!myIsPreview && isArtifactResolutionQuerySupported) { + Class jvmLibrary = null; + try { + jvmLibrary = (Class)Class.forName("org.gradle.jvm.JvmLibrary"); + } + catch (ClassNotFoundException ignored) { + } + if (jvmLibrary == null) { + try { + jvmLibrary = (Class)Class.forName("org.gradle.runtime.jvm.JvmLibrary"); + } + catch (ClassNotFoundException ignored) { + } + } + + if (jvmLibrary != null) { + List> artifactTypes = new ArrayList>(); + + if (myDownloadSources) { artifactTypes.add(SourcesArtifact.class); } + if (myDownloadJavadoc) { artifactTypes.add(JavadocArtifact.class); } + + + Set resolvedArtifacts = + configuration.getResolvedConfiguration().getLenientConfiguration().getArtifacts(Specs.SATISFIES_ALL); + + Multimap artifactMap = ArrayListMultimap.create(); + + for (ResolvedArtifact artifact : resolvedArtifacts) { + artifactMap.put(artifact.getModuleVersion().getId(), artifact); + } + + + boolean isBuildScriptConfiguration = myProject.getBuildscript().getConfigurations().contains(configuration); + //noinspection GroovyAssignabilityCheck + DependencyHandler dependencyHandler = isBuildScriptConfiguration ? myProject.getBuildscript().getDependencies() : myProject.getDependencies(); + + List components = new ArrayList(); + for (ResolvedArtifact artifact : resolvedArtifacts) { + if (!isProjectDependencyArtifact(artifact)) { + components.add(toComponentIdentifier(artifact.getModuleVersion().getId())); + } + } + + Set componentResults = dependencyHandler.createArtifactResolutionQuery() + .forComponents(components) + .withArtifacts(jvmLibrary, artifactTypes.toArray(new Class[artifactTypes.size()])) + .execute() + .getResolvedComponents(); + + Map componentResultsMap = + new HashMap(); + + for (ComponentArtifactsResult artifactsResult : componentResults) { + componentResultsMap.put(artifactsResult.getId(), artifactsResult); + } + + Set processedConfigurations = new HashSet(); + Multimap configurationProjectDependencies = + projectDeps(configuration, ArrayListMultimap.create(), processedConfigurations); + + ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult(); + if(!configuration.getResolvedConfiguration().hasError()) { + Set fileDeps = new LinkedHashSet(configuration.getIncoming().getFiles().getFiles()); + + for (ResolvedArtifact artifact : artifactMap.values()) { + fileDeps.remove(artifact.getFile()); + } + + for (ProjectDependency dep : configurationProjectDependencies.values()) { + final Set intersection = new HashSet(Sets.intersection(fileDeps, dep.resolve())); + if (!intersection.isEmpty()) { + DefaultFileCollectionDependency fileCollectionDependency = new DefaultFileCollectionDependency(intersection); + fileCollectionDependency.setScope(scope); + result.add(fileCollectionDependency); + fileDeps.removeAll(intersection); + } + } + + for (File file : fileDeps) { + DefaultFileCollectionDependency fileCollectionDependency = new DefaultFileCollectionDependency(Collections.singleton(file)); + fileCollectionDependency.setScope(scope); + result.add(fileCollectionDependency); + } + } + + DependencyResultsTransformer dependencyResultsTransformer = + new DependencyResultsTransformer(artifactMap, componentResultsMap, configurationProjectDependencies, scope); + result.addAll(dependencyResultsTransformer.transform(resolutionResult.getRoot().getDependencies())); + + resolvedFileDependencies.addAll(dependencyResultsTransformer.resolvedDepsFiles); + } + } + + if (myIsPreview || !isArtifactResolutionQuerySupported) { + Set projectDependencies = findDependencies(configuration, configuration.getAllDependencies(), scope); + result.addAll(projectDependencies); + } + Set fileDependencies = findAllFileDependencies(configuration.getAllDependencies(), scope); + // TODO investigate resolvedFileDependencies content + fileDependencies.removeAll(resolvedFileDependencies); + result.addAll(fileDependencies); + + return new ExternalDepsResolutionResult(new ArrayList(result), resolvedFileDependencies); + } + + Multimap projectDeps(Configuration conf, + Multimap map, + Set processedConfigurations) { + if(!processedConfigurations.add(conf)) { + return map; + } + for (Dependency dep : conf.getIncoming().getDependencies()) { + if (dep instanceof ProjectDependency) { + map.put(toComponentIdentifier(dep.getGroup(), dep.getName(), dep.getVersion()), (ProjectDependency)dep); + projectDeps(getTargetConfiguration((ProjectDependency)dep), map, processedConfigurations); + } + } + return map; + } + + boolean containsAll(Configuration cfg, Collection files) { + for (File file : files) { + if (!cfg.contains(file)) { + return false; + } + } + return true; + } + + @Override + public Collection resolveDependencies(@NotNull SourceSet sourceSet) { + Collection result = new ArrayList(); + + // resolve compile dependencies + boolean isMainSourceSet = sourceSet.getName().equals(SourceSet.MAIN_SOURCE_SET_NAME); + String deprecatedCompileConfigurationName = isMainSourceSet ? "compile" : GUtil.toCamelCase(sourceSet.getName()) + "Compile"; + Configuration deprecatedCompileConfiguration = myProject.getConfigurations().findByName(deprecatedCompileConfigurationName); + String compileConfigurationName = sourceSet.getCompileConfigurationName(); + Configuration compileClasspathConfiguration = myProject.getConfigurations().findByName(compileConfigurationName + "Classpath"); + Configuration originCompileConfiguration = myProject.getConfigurations().findByName(compileConfigurationName); + Configuration compileConfiguration = compileClasspathConfiguration != null ? compileClasspathConfiguration : originCompileConfiguration; + Configuration compileOnlyConfiguration = + isJavaLibraryPluginSupported ? myProject.getConfigurations().findByName(sourceSet.getCompileOnlyConfigurationName()) : null; + + String compileScope = "COMPILE"; + ExternalDepsResolutionResult externalDepsResolutionResult = resolveDependencies(compileConfiguration, compileScope); + Collection compileDependencies = externalDepsResolutionResult.getExternalDeps(); + Collection resolvedCompileFileDependencies = externalDepsResolutionResult.getResolvedFiles(); + + // resolve runtime dependencies + String runtimeConfigurationName = sourceSet.getRuntimeConfigurationName(); + Configuration runtimeClasspathConfiguration = myProject.getConfigurations().findByName(runtimeConfigurationName + "Classpath"); + Configuration originRuntimeConfiguration = myProject.getConfigurations().findByName(runtimeConfigurationName); + Configuration runtimeConfiguration = runtimeClasspathConfiguration != null ? runtimeClasspathConfiguration : originRuntimeConfiguration; + + String runtimeScope = "RUNTIME"; + externalDepsResolutionResult = resolveDependencies(runtimeConfiguration, runtimeScope); + Collection runtimeDependencies = externalDepsResolutionResult.getExternalDeps(); + Collection resolvedRuntimeFileDependencies = externalDepsResolutionResult.getResolvedFiles(); + + + String providedScope = "PROVIDED"; + Multimap resolvedMap = ArrayListMultimap.create(); + + boolean checkCompileOnlyDeps = compileClasspathConfiguration != null && !originCompileConfiguration.getResolvedConfiguration().hasError(); + + for (ExternalDependency dep : new DependencyTraverser(compileDependencies)) { + final Collection resolvedFiles = resolve(dep); + resolvedMap.put(resolvedFiles, dep); + + // since version 3.4 compileOnly no longer extends compile + // so, we can use compileOnly configuration for the check + if (isJavaLibraryPluginSupported) { + if (compileOnlyConfiguration != null && containsAll(compileOnlyConfiguration, resolvedFiles)) { + // deprecated 'compile' configuration still can be used + if (deprecatedCompileConfiguration == null || !containsAll(deprecatedCompileConfiguration, resolvedFiles)) { + ((AbstractExternalDependency)dep).setScope(providedScope); + } + } + } + else { + if (checkCompileOnlyDeps && ! containsAll(originCompileConfiguration, resolvedFiles) && + !containsAll(runtimeConfiguration, resolvedFiles)) { + ((AbstractExternalDependency)dep).setScope(providedScope); + } + } + } + + + Multimap resolvedRuntimeMap = ArrayListMultimap.create(); + + for (ExternalDependency dep : new DependencyTraverser(runtimeDependencies)) { + Collection dependencies = resolvedMap.get(resolve(dep)); + if (dependencies != null && !dependencies.isEmpty() && dep.getDependencies().isEmpty()) { + runtimeDependencies.remove(dep); + ((AbstractExternalDependency)dep).setScope(compileScope); + for (ExternalDependency dependency : dependencies) { + ((AbstractExternalDependency)dependency).setScope(compileScope); + } + } + else { + if (dependencies != null && !dependencies.isEmpty()) { + ((AbstractExternalDependency)dep).setScope(compileScope); + } + resolvedRuntimeMap.put(resolve(dep), dep); + } + } + + resolvedMap.putAll(resolvedRuntimeMap); + + result.addAll(compileDependencies); + result.addAll(runtimeDependencies); + + + result = Lists.newArrayList(filter(result, not(isNull()))); + + // merge file dependencies + List jvmLanguages = Lists.newArrayList("Java", "Groovy", "Scala"); + final String sourceSetCompileTaskPrefix = sourceSet.getName() == "main" ? "" : sourceSet.getName(); + + List compileTasks = Lists.transform(jvmLanguages, new Function() { + @Override + public String apply(String s) { + return "compile" + capitalize(sourceSetCompileTaskPrefix) + s; + } + }); + + Map compileClasspathOrder = new LinkedHashMap(); + Set compileClasspathFiles = new LinkedHashSet(); + + for (String task : compileTasks) { + Task compileTask = myProject.getTasks().findByName(task); + if (compileTask instanceof AbstractCompile) { + try { + List files = new ArrayList(((AbstractCompile)compileTask).getClasspath().getFiles()); + // TODO is this due to ordering? + files.removeAll(compileClasspathFiles); + compileClasspathFiles.addAll(files); + } catch (Exception e) { + // ignore + } + } + } + + try { + compileClasspathFiles = compileClasspathFiles.isEmpty() ? sourceSet.getCompileClasspath().getFiles() : compileClasspathFiles; + } catch (Exception e) { + // ignore + } + + int order = 0; + for (File file : compileClasspathFiles) { + compileClasspathOrder.put(file, order++); + } + + Map runtimeClasspathOrder = new LinkedHashMap(); + order = 0; + Set runtimeClasspathFiles = new LinkedHashSet(); + try { + Set files = sourceSet.getRuntimeClasspath().getFiles(); + for (File file : files) { + runtimeClasspathOrder.put(file, order++); + } + runtimeClasspathFiles.addAll(files); + } catch (Exception e) { + // ignore + } + + runtimeClasspathFiles.removeAll(compileClasspathFiles); + runtimeClasspathFiles.removeAll(sourceSet.getOutput().getFiles()); + compileClasspathFiles.removeAll(sourceSet.getOutput().getFiles()); + + Multimap resolvedDependenciesMap = ArrayListMultimap.create(); + resolvedDependenciesMap.putAll(compileScope, resolvedCompileFileDependencies); + resolvedDependenciesMap.putAll(runtimeScope, resolvedRuntimeFileDependencies); + Project rootProject = myProject.getRootProject(); + + for (ExternalDependency dependency : new DependencyTraverser(result)) { + String scope = dependency.getScope(); + order = -1; + if (dependency instanceof ExternalProjectDependency) { + ExternalProjectDependency projectDependency = (ExternalProjectDependency)dependency; + + Project project = rootProject.findProject(projectDependency.getProjectPath()); + if (project != null) { + Configuration configuration = project.getConfigurations().findByName(projectDependency.getConfigurationName()); + + if (configuration != null) { + for (File file : configuration.getAllArtifacts().getFiles().getFiles()) { + resolvedDependenciesMap.put(scope, file); + Map classpathOrderMap = scope == compileScope ? compileClasspathOrder : + scope == runtimeScope ? runtimeClasspathOrder : null; + if (classpathOrderMap != null) { + Integer fileOrder = classpathOrderMap.get(file); + if (fileOrder != null && (order == -1 || fileOrder < order)) { + order = fileOrder; + } + } + } + } + + //noinspection GrUnresolvedAccess + if (project.hasProperty("sourceSets") + && (project.property("sourceSets") instanceof SourceSetContainer) + && ((SourceSetContainer)project.property("sourceSets")).findByName("main") != null) { + //noinspection GrUnresolvedAccess + addSourceSetOutputDirsAsSingleEntryLibraries(result, + ((SourceSetContainer)project.property("sourceSets")).findByName("main"), + runtimeClasspathOrder, scope); + } + } + } else if (dependency instanceof ExternalLibraryDependency) { + final File file = ((ExternalLibraryDependency)dependency).getFile(); + resolvedDependenciesMap.put(scope, file); + Map classpathOrderMap = scope == compileScope ? compileClasspathOrder : + scope == runtimeScope ? runtimeClasspathOrder : null; + if (classpathOrderMap != null) { + Integer fileOrder = classpathOrderMap.get(file); + order = fileOrder != null ? fileOrder : -1; + } + } else if (dependency instanceof FileCollectionDependency) { + for (File file : ((FileCollectionDependency)dependency).getFiles()) { + resolvedDependenciesMap.put(scope, file); + Map classpathOrderMap = scope == compileScope ? compileClasspathOrder : + scope == runtimeScope ? runtimeClasspathOrder : null; + if (classpathOrderMap != null) { + Integer fileOrder = classpathOrderMap.get(file); + if (fileOrder != null && (order == -1 || fileOrder < order)) { + order = fileOrder; + } + if (order == 0) break; + } + } + } + + if (dependency instanceof AbstractExternalDependency) { + ((AbstractExternalDependency)dependency).setClasspathOrder(order); + } + } + + compileClasspathFiles.removeAll(resolvedDependenciesMap.get(compileScope)); + compileClasspathFiles.removeAll(resolvedDependenciesMap.get(providedScope)); + runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(runtimeScope)); + runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(compileScope)); + runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(providedScope)); + + Collection fileDependencies = new ArrayList(); + mapFileDependencies(runtimeClasspathFiles, runtimeScope, fileDependencies); + mapFileDependencies(compileClasspathFiles, compileScope, fileDependencies); + + for (ExternalDependency dependency : fileDependencies) { + String scope = dependency.getScope(); + order = -1; + if (dependency instanceof ExternalLibraryDependency) { + Map classpathOrderMap = scope == compileScope ? compileClasspathOrder : + scope == runtimeScope ? runtimeClasspathOrder : null; + if (classpathOrderMap != null) { + Integer fileOrder = classpathOrderMap.get(((ExternalLibraryDependency)dependency).getFile()); + order = fileOrder != null ? fileOrder : -1; + } + } + if (dependency instanceof AbstractExternalDependency) { + ((AbstractExternalDependency)dependency).setClasspathOrder(order); + } + } + + result.addAll(fileDependencies); + + if (!compileClasspathFiles.isEmpty()) { + final DefaultFileCollectionDependency compileClasspathFilesDependency = new DefaultFileCollectionDependency(compileClasspathFiles); + compileClasspathFilesDependency.setScope(compileScope); + + order = -1; + + for (File file : compileClasspathFiles) { + Integer fileOrder = compileClasspathOrder.get(file); + if (fileOrder != null && (order == -1 || fileOrder < order)) { + order = fileOrder; + } + if (order == 0) break; + } + + if (order != -1) { + compileClasspathFilesDependency.setClasspathOrder(order); + } + + result.add(compileClasspathFilesDependency); + for (File file : compileClasspathFiles) { + SourceSet outputDirSourceSet = mySourceSetFinder.findByArtifact(file.getPath()); + if(outputDirSourceSet != null) { + addSourceSetOutputDirsAsSingleEntryLibraries(result, outputDirSourceSet, compileClasspathOrder, compileScope); + } + } + } + + if (!runtimeClasspathFiles.isEmpty()) { + final DefaultFileCollectionDependency runtimeClasspathFilesDependency = new DefaultFileCollectionDependency(runtimeClasspathFiles); + runtimeClasspathFilesDependency.setScope(runtimeScope); + + order = -1; + for (File file : runtimeClasspathFiles) { + Integer fileOrder = runtimeClasspathOrder.get(file); + if (fileOrder != null && (order == -1 || fileOrder < order)) { + order = fileOrder; + } + if (order == 0) break; + } + + runtimeClasspathFilesDependency.setClasspathOrder(order); + result.add(runtimeClasspathFilesDependency); + + for (File file : runtimeClasspathFiles) { + SourceSet outputDirSourceSet = mySourceSetFinder.findByArtifact(file.getPath()); + if (outputDirSourceSet != null) { + addSourceSetOutputDirsAsSingleEntryLibraries(result, outputDirSourceSet, runtimeClasspathOrder, runtimeScope); + } + } + } + + addSourceSetOutputDirsAsSingleEntryLibraries(result, sourceSet, runtimeClasspathOrder, runtimeScope); + + // handle provided dependencies + final Set providedConfigurations = new LinkedHashSet(); + resolvedMap = ArrayListMultimap.create(); + for (ExternalDependency dep : new DependencyTraverser(result)) { + resolvedMap.put(resolve(dep), dep); + } + + if (sourceSet.getName().equals("main") && myProject.getPlugins().findPlugin(WarPlugin.class) != null) { + providedConfigurations.add(myProject.getConfigurations().findByName("providedCompile")); + providedConfigurations.add(myProject.getConfigurations().findByName("providedRuntime")); + } + + final IdeaPlugin ideaPlugin = myProject.getPlugins().findPlugin(IdeaPlugin.class); + + if (ideaPlugin != null) { + Map>> scopes = ideaPlugin.getModel().getModule().getScopes(); + Map> providedPlusScopes = scopes.get(providedScope); + + if (providedPlusScopes != null && providedPlusScopes.get("plus") != null) { + Iterable ideaPluginProvidedConfigurations = filter(providedPlusScopes.get("plus"), new Predicate() { + @Override + + public boolean apply(Configuration cfg) { + // filter default 'compileClasspath' for slight optimization since it has been already processed as compile dependencies + return !cfg.getName().equals("compileClasspath") + // since gradle 3.4 'idea' plugin PROVIDED scope.plus contains 'providedCompile' and 'providedRuntime' configurations + // see https://github.com/gradle/gradle/commit/c46897ae840c5ebb32946009c83d861ee194ab96#diff-0fa13ec419e839ef2d355b7feb88b815R432 + && !providedConfigurations.contains(cfg); + } + }); + + for (Configuration configuration : ideaPluginProvidedConfigurations) { + Collection providedDependencies = resolveDependencies(configuration, providedScope).getExternalDeps(); + for(ExternalDependency it : new DependencyTraverser(providedDependencies)) { + resolvedMap.put(resolve(it), it); + } + result.addAll(providedDependencies); + } + } + } + + for (Configuration cfg : providedConfigurations) { + Collection providedDependencies = resolveDependencies(cfg, providedScope).getExternalDeps(); + for (ExternalDependency dep : new DependencyTraverser(providedDependencies)) { + Collection dependencies = resolvedMap.get(resolve(dep)); + if (!dependencies.isEmpty()) { + if (dep.getDependencies().isEmpty()) { + providedDependencies.remove(dep); + } + for (ExternalDependency depForScope: dependencies) { + ((AbstractExternalDependency)depForScope).setScope(providedScope); + } + } else { + resolvedMap.put(resolve(dep), dep); + } + } + result.addAll(providedDependencies); + } + + return removeDuplicates(resolvedMap, result); + } + + private static List removeDuplicates(Multimap resolvedMap, Collection result) { + + for (Collection val : resolvedMap.asMap().values()) { + List toRemove = new ArrayList(); + boolean isCompileScope = false; + boolean isProvidedScope = false; + + for (ExternalDependency dep : val) { + if (dep.getDependencies().isEmpty()) { + toRemove.add(dep); + if (dep.getScope().equals("COMPILE")) { + isCompileScope = true; + } else if (dep.getScope().equals("PROVIDED")) { + isProvidedScope = true; + } + } + } + + if (toRemove.size() != val.size()) { + result.removeAll(toRemove); + } else if (toRemove.size() > 1) { + toRemove = toRemove.subList(1, toRemove.size()); + result.removeAll(toRemove); + } + + if (!toRemove.isEmpty()) { + List retained = new ArrayList(val); + retained.removeAll(toRemove); + if(!retained.isEmpty()) { + ExternalDependency retainedDependency = retained.iterator().next(); + if(retainedDependency instanceof AbstractExternalDependency && !retainedDependency.getScope().equals("COMPILE")) { + if (isCompileScope) { + ((AbstractExternalDependency)retainedDependency).setScope("COMPILE"); + } else if (isProvidedScope) { + ((AbstractExternalDependency)retainedDependency).setScope("PROVIDED"); + } + } + } + } + } + + + return Lists.newArrayList(filter(result, not(isNull()))); + } + + @NotNull + static Collection resolve(ExternalDependency dependency) { + if (dependency instanceof ExternalLibraryDependency) { + return Collections.singleton(((ExternalLibraryDependency)dependency).getFile()); + } else if (dependency instanceof FileCollectionDependency) { + return ((FileCollectionDependency)dependency).getFiles(); + } else if (dependency instanceof ExternalMultiLibraryDependency) { + return ((ExternalMultiLibraryDependency)dependency).getFiles(); + } else if (dependency instanceof ExternalProjectDependency) { + return ((ExternalProjectDependency)dependency).getProjectDependencyArtifacts(); + } + return Collections.emptySet(); + } + + private static void addSourceSetOutputDirsAsSingleEntryLibraries( + Collection dependencies, + SourceSet sourceSet, + Map classpathOrder, + String scope) { + Set runtimeOutputDirs = sourceSet.getOutput().getDirs().getFiles(); + for (File dir : runtimeOutputDirs) { + DefaultFileCollectionDependency runtimeOutputDirsDependency = new DefaultFileCollectionDependency(Collections.singleton(dir)); + runtimeOutputDirsDependency.setScope(scope); + Integer fileOrder = classpathOrder.get(dir); + runtimeOutputDirsDependency.setClasspathOrder(fileOrder != null ? fileOrder : -1); + dependencies.add(runtimeOutputDirsDependency); + } + } + + + @Nullable + ExternalLibraryDependency resolveLibraryByPath(File file, String scope) { + File modules2Dir = new File(myProject.getGradle().getGradleUserHomeDir(), "caches/modules-2/files-2.1"); + return resolveLibraryByPath(file, modules2Dir, scope); + } + + @Nullable + static ExternalLibraryDependency resolveLibraryByPath(File file, File modules2Dir, String scope) { + File sourcesFile = null; + try { + String modules2Path = modules2Dir.getCanonicalPath(); + String filePath = file.getCanonicalPath(); + + if (filePath.startsWith(modules2Path)) { + List parents = new ArrayList(); + File parent = file.getParentFile(); + while(parent != null && !parent.getName().equals(modules2Dir.getName())) { + parents.add(parent); + parent = parent.getParentFile(); + } + + File groupDir = parents.get(parents.size() - 1); + File artifactDir = parents.get(parents.size() - 2); + File versionDir = parents.get(parents.size() - 3); + + File parentFile = versionDir; + if (parentFile != null) { + File[] hashDirs = parentFile.listFiles(); + if (hashDirs != null) { + for (File hashDir : hashDirs) { + File[] sourcesJars = hashDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith("sources.jar"); + } + }); + + if (sourcesJars != null && sourcesJars.length > 0) { + sourcesFile = sourcesJars[0]; + break; + } + } + + String packaging = resolvePackagingType(file); + String classifier = resolveClassifier(artifactDir.getName(), versionDir.getName(), file); + + DefaultExternalLibraryDependency defaultDependency = new DefaultExternalLibraryDependency(); + + defaultDependency.setName(artifactDir.getName()); + defaultDependency.setGroup(groupDir.getName()); + defaultDependency.setPackaging(packaging); + defaultDependency.setClassifier(classifier); + defaultDependency.setVersion(versionDir.getName()); + defaultDependency.setFile(file); + defaultDependency.setSource(sourcesFile); + defaultDependency.setScope(scope); + + return defaultDependency; + } + } + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + + return null; + } + + void mapFileDependencies(Set fileDependencies, String scope, Collection dependencies) { + File modules2Dir = new File(myProject.getGradle().getGradleUserHomeDir(), "caches/modules-2/files-2.1"); + List toRemove = new ArrayList(); + for (File file : fileDependencies) { + ExternalLibraryDependency libraryDependency = resolveLibraryByPath(file, modules2Dir, scope); + if (libraryDependency != null) { + dependencies.add(libraryDependency); + toRemove.add(file); + } else { + //noinspection GrUnresolvedAccess + String name = getNameWithoutExtension(file); + File sourcesFile = new File(file.getParentFile(), name + "-sources.jar"); + if (sourcesFile.exists()) { + libraryDependency = new DefaultExternalLibraryDependency(); + DefaultExternalLibraryDependency defLD = (DefaultExternalLibraryDependency)libraryDependency; + defLD.setFile(file); + defLD.setSource(sourcesFile); + defLD.setScope(scope); + + dependencies.add(libraryDependency); + toRemove.add(file); + } + } + } + + fileDependencies.removeAll(toRemove); + } + + @NotNull + static String resolvePackagingType(File file) { + if (file == null) return "jar"; + String path = file.getPath(); + int index = path.lastIndexOf('.'); + if (index < 0) return "jar"; + return path.substring(index + 1); + } + + @Nullable + static String resolveClassifier(String name, String version, File file) { + String libraryFileName = getNameWithoutExtension(file); + final String mavenLibraryFileName = name + "-" + version; + if (!mavenLibraryFileName.equals(libraryFileName)) { + Matcher matcher = Pattern.compile(name + "-" + version + "-(.*)").matcher(libraryFileName); + if (matcher.matches()) { + return matcher.group(1); + } + } + return null; + } + + static String getNameWithoutExtension(File file) { + if (file == null) return null; + String name = file.getName(); + int i = name.lastIndexOf('.'); + if (i != -1) { + name = name.substring(0, i); + } + return name; + } + + public ModuleComponentIdentifier toComponentIdentifier(ModuleVersionIdentifier id) { + return new ModuleComponentIdentifierImpl(id.getGroup(), id.getName(), id.getVersion()); + } + + public ModuleComponentIdentifier toComponentIdentifier(@NotNull String group, @NotNull String module, @NotNull String version) { + return new ModuleComponentIdentifierImpl(group, module, version); + } + + private static Set findAllFileDependencies(Collection dependencies, String scope) { + Set result = new LinkedHashSet(); + + for (Dependency dep : dependencies) { + try { + if (dep instanceof SelfResolvingDependency && !(dep instanceof ProjectDependency)) { + Set files = ((SelfResolvingDependency)dep).resolve(); + if (files != null && !files.isEmpty()) { + AbstractExternalDependency dependency = new DefaultFileCollectionDependency(files); + dependency.setScope(scope); + result.add(dependency); + } + } + } + catch (Exception e) { + // ignore + } + } + + return result; + } + + private Set findDependencies( + Configuration configuration, + Collection dependencies, + String scope) { + Set result = new LinkedHashSet(); + + Set resolvedArtifacts = myIsPreview ? new HashSet() : + configuration.getResolvedConfiguration().getLenientConfiguration() + .getArtifacts(Specs.SATISFIES_ALL); + + Multimap artifactMap = ArrayListMultimap.create(); + for (ResolvedArtifact artifact : resolvedArtifacts) { + artifactMap.put(toMyModuleIdentifier(artifact.getModuleVersion().getId()), artifact); + } + + for (Dependency it : dependencies) { + try { + if (it instanceof ProjectDependency) { + Project project = ((ProjectDependency)it).getDependencyProject(); + Configuration targetConfiguration = getTargetConfiguration((ProjectDependency)it); + + DefaultExternalProjectDependency projectDependency = new DefaultExternalProjectDependency(); + projectDependency.setName(project.getName()); + projectDependency.setGroup(project.getGroup().toString()); + projectDependency.setVersion(project.getVersion().toString()); + projectDependency.setScope(scope); + projectDependency.setProjectPath(project.getPath()); + projectDependency.setConfigurationName(targetConfiguration.getName()); + projectDependency.setProjectDependencyArtifacts(targetConfiguration.getAllArtifacts().getFiles().getFiles()); + + result.add(projectDependency); + } else if (it != null) { + Collection artifactsResult = artifactMap.get(toMyModuleIdentifier(it.getName(), it.getGroup())); + if (artifactsResult != null && !artifactsResult.isEmpty()) { + ResolvedArtifact artifact = artifactsResult.iterator().next(); + String packaging = artifact.getExtension() != null ? artifact.getExtension() : "jar"; + String classifier = artifact.getClassifier(); + final ExternalLibraryDependency resolvedDep = resolveLibraryByPath(artifact.getFile(), scope); + File sourcesFile = resolvedDep == null ? null : resolvedDep.getSource(); + + DefaultExternalLibraryDependency libraryDependency = new DefaultExternalLibraryDependency(); + libraryDependency.setName(it.getName()); + libraryDependency.setGroup(it.getGroup()); + libraryDependency.setPackaging(packaging); + libraryDependency.setClassifier(classifier); + libraryDependency.setVersion(artifact.getModuleVersion().getId().getVersion()); + libraryDependency.setScope(scope); + libraryDependency.setFile(artifact.getFile()); + libraryDependency.setSource(sourcesFile); + + result.add(libraryDependency); + } else { + if (!(it instanceof SelfResolvingDependency) && !myIsPreview) { + final DefaultUnresolvedExternalDependency dependency = new DefaultUnresolvedExternalDependency(); + dependency.setName(it.getName()); + dependency.setGroup(it.getGroup()); + dependency.setVersion(it.getVersion()); + dependency.setScope(scope); + dependency.setFailureMessage("Could not find " + it.getGroup() + ":" + it.getName() + ":" + it.getVersion()); + result.add(dependency); + } + } + } + } + catch (Exception e) { + // ignore + } + } + + return result; + } + + private static Configuration getTargetConfiguration(ProjectDependency projectDependency) { + + try { + return !is4OrBetter ? (Configuration)projectDependency.getClass().getMethod("getProjectConfiguration").invoke(projectDependency) : + projectDependency.getDependencyProject().getConfigurations().getByName(projectDependency.getTargetConfiguration() != null + ? projectDependency.getTargetConfiguration() + : "default"); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + class DependencyResultsTransformer { + Collection handledDependencyResults; + Multimap artifactMap; + Map componentResultsMap; + Multimap configurationProjectDependencies; + String scope; + Set resolvedDepsFiles = new HashSet(); + + DependencyResultsTransformer( + Multimap artifactMap, + Map componentResultsMap, + Multimap configurationProjectDependencies, + String scope) { + this.handledDependencyResults = Lists.newArrayList(); + this.artifactMap = artifactMap; + this.componentResultsMap = componentResultsMap; + this.configurationProjectDependencies = configurationProjectDependencies; + this.scope = scope; + } + + Set transform(Collection dependencyResults) { + + Set dependencies = new LinkedHashSet(); + for (DependencyResult dependencyResult : dependencyResults) { + + // dependency cycles check + if (!handledDependencyResults.contains(dependencyResult)) { + handledDependencyResults.add(dependencyResult); + + if (dependencyResult instanceof ResolvedDependencyResult) { + ResolvedComponentResult componentResult = ((ResolvedDependencyResult)dependencyResult).getSelected(); + ComponentSelector componentSelector = dependencyResult.getRequested(); + ModuleComponentIdentifier componentIdentifier = toComponentIdentifier(componentResult.getModuleVersion()); + + String name = componentResult.getModuleVersion().getName(); + String group = componentResult.getModuleVersion().getGroup(); + String version = componentResult.getModuleVersion().getVersion(); + String selectionReason = componentResult.getSelectionReason().getDescription(); + + boolean resolveFromArtifacts = componentSelector instanceof ModuleComponentSelector; + + if (componentSelector instanceof ProjectComponentSelector) { + Collection projectDependencies = configurationProjectDependencies.get(componentIdentifier); + Collection dependencyConfigurations; + if (projectDependencies.isEmpty()) { + Project dependencyProject = myProject.findProject(((ProjectComponentSelector)componentSelector).getProjectPath()); + if (dependencyProject != null) { + Configuration dependencyProjectConfiguration = + dependencyProject.getConfigurations().getByName(Dependency.DEFAULT_CONFIGURATION); + dependencyConfigurations = Collections.singleton(dependencyProjectConfiguration); + } else { + dependencyConfigurations = Collections.emptySet(); + resolveFromArtifacts = true; + selectionReason = "composite build substitution"; + } + } else { + dependencyConfigurations = new ArrayList(); + for (ProjectDependency dependency : projectDependencies) { + dependencyConfigurations.add(getTargetConfiguration(dependency)); + } + } + + for (Configuration it : dependencyConfigurations) { + if (it.getName().equals(Dependency.DEFAULT_CONFIGURATION)) { + DefaultExternalProjectDependency dependency = new DefaultExternalProjectDependency(); + dependency.setName( name); + dependency.setGroup(group); + dependency.setVersion(version); + dependency.setScope(scope); + dependency.setSelectionReason(selectionReason); + dependency.setProjectPath(((ProjectComponentSelector)componentSelector).getProjectPath()); + dependency.setConfigurationName(it.getName()); + dependency.setProjectDependencyArtifacts(it.getAllArtifacts().getFiles().getFiles()); + + resolvedDepsFiles.addAll(dependency.getProjectDependencyArtifacts()); + + if (it.getArtifacts().size() == 1) { + PublishArtifact publishArtifact = it.getAllArtifacts().iterator().next(); + dependency.setClassifier(publishArtifact.getClassifier()); + dependency.setPackaging(publishArtifact.getExtension() != null ? publishArtifact.getExtension() : "jar"); + } + + if (!componentResult.equals(dependencyResult.getFrom())) { + dependency.getDependencies().addAll( + transform(componentResult.getDependencies()) + ); + } + dependencies.add(dependency); + } + else { + DefaultExternalProjectDependency dependency = new DefaultExternalProjectDependency(); + dependency.setName(name); + dependency.setGroup(group); + dependency.setVersion(version); + dependency.setScope(scope); + dependency.setSelectionReason(selectionReason); + dependency.setProjectPath(((ProjectComponentSelector)componentSelector).getProjectPath()); + dependency.setConfigurationName(it.getName()); + dependency.setProjectDependencyArtifacts(it.getAllArtifacts().getFiles().getFiles()); + + resolvedDepsFiles.addAll(dependency.getProjectDependencyArtifacts()); + + if (it.getArtifacts().size() == 1) { + PublishArtifact publishArtifact = it.getAllArtifacts().iterator().next(); + dependency.setClassifier(publishArtifact.getClassifier()); + dependency.setPackaging(publishArtifact.getExtension() != null ? publishArtifact.getExtension() : "jar"); + } + + if (!componentResult.equals(dependencyResult.getFrom())) { + + dependency.getDependencies().addAll( + transform(componentResult.getDependencies()) + ); + } + + dependencies.add(dependency); + + List files = new ArrayList(); + PublishArtifactSet artifacts = it.getArtifacts(); + if (artifacts != null && !artifacts.isEmpty()) { + PublishArtifact artifact = artifacts.iterator().next(); + final MetaProperty taskProperty = DefaultGroovyMethods.hasProperty(artifact, "archiveTask"); + if (taskProperty != null && (taskProperty.getProperty(artifact) instanceof AbstractArchiveTask)) { + + AbstractArchiveTask archiveTask = (AbstractArchiveTask)taskProperty.getProperty(artifact); + resolvedDepsFiles.add(new File(archiveTask.getDestinationDir(), archiveTask.getArchiveName())); + + + try { + final Method mainSpecGetter = AbstractCopyTask.class.getDeclaredMethod("getMainSpec"); + mainSpecGetter.setAccessible(true); + Object mainSpec = mainSpecGetter.invoke(archiveTask); + + final List sourcePathGetters = + DefaultGroovyMethods.respondsTo(mainSpec, "getSourcePaths", new Object[]{}); + if (!sourcePathGetters.isEmpty()) { + Set sourcePaths = (Set)sourcePathGetters.get(0).doMethodInvoke(mainSpec, new Object[]{}); + if (sourcePaths != null) { + for (Object path : sourcePaths) { + if (path instanceof String) { + File file = new File((String)path); + if (file.isAbsolute()) { + files.add(file); + } + } + else if (path instanceof SourceSetOutput) { + files.addAll(((SourceSetOutput)path).getFiles()); + } + } + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + if (!files.isEmpty()) { + final DefaultFileCollectionDependency fileCollectionDependency = new DefaultFileCollectionDependency(files); + fileCollectionDependency.setScope(scope); + dependencies.add(fileCollectionDependency); + resolvedDepsFiles.addAll(files); + } + } + } + } + + if (resolveFromArtifacts) { + Collection artifacts = artifactMap.get(componentResult.getModuleVersion()); + + if (artifacts != null && artifacts.isEmpty()) { + dependencies.addAll( + transform(componentResult.getDependencies()) + ); + } + + boolean first = true; + + if (artifacts != null) { + for (ResolvedArtifact artifact: artifacts) { + String packaging = artifact.getExtension() != null ? artifact.getExtension() : "jar"; + String classifier = artifact.getClassifier(); + final ExternalDependency dependency; + if (isProjectDependencyArtifact(artifact)) { + ProjectComponentIdentifier artifactComponentIdentifier = + (ProjectComponentIdentifier)artifact.getId().getComponentIdentifier(); + + dependency = new DefaultExternalProjectDependency(); + DefaultExternalProjectDependency dDep = (DefaultExternalProjectDependency)dependency; + dDep.setName(name); + dDep.setGroup(group); + dDep.setVersion(version); + dDep.setScope(scope); + dDep.setSelectionReason(selectionReason); + dDep.setProjectPath(artifactComponentIdentifier.getProjectPath()); + dDep.setConfigurationName(Dependency.DEFAULT_CONFIGURATION); + + List files = new ArrayList(); + for (ResolvedArtifact resolvedArtifact : artifactMap.get(componentResult.getModuleVersion())) { + files.add(resolvedArtifact.getFile()); + } + dDep.setProjectDependencyArtifacts(files); + resolvedDepsFiles.addAll(dDep.getProjectDependencyArtifacts()); + } + + else { + dependency = new DefaultExternalLibraryDependency(); + DefaultExternalLibraryDependency dDep = (DefaultExternalLibraryDependency)dependency; + dDep.setName(name); + dDep.setGroup(group); + dDep.setPackaging(packaging); + dDep.setClassifier(classifier); + dDep.setVersion(version); + dDep.setScope(scope); + dDep.setSelectionReason(selectionReason); + dDep.setFile(artifact.getFile()); + + ComponentArtifactsResult artifactsResult = componentResultsMap.get(componentIdentifier); + if (artifactsResult != null) { + final Set sourceArtifacts = artifactsResult.getArtifacts(SourcesArtifact.class); + ResolvedArtifactResult sourcesResult = sourceArtifacts != null && !sourceArtifacts.isEmpty() + ? (ResolvedArtifactResult)find(sourceArtifacts, Predicates.instanceOf(ResolvedArtifactResult.class)) + : null; + if (sourcesResult != null) { + ((DefaultExternalLibraryDependency)dependency).setSource(sourcesResult.getFile()); + } + + + final Set javadocArtifacts = artifactsResult.getArtifacts(JavadocArtifact.class); + ResolvedArtifactResult javadocResult = javadocArtifacts != null && !javadocArtifacts.isEmpty() + ? (ResolvedArtifactResult)find(javadocArtifacts, Predicates.instanceOf(ResolvedArtifactResult.class)) + : null; + if (javadocResult != null) { + ((DefaultExternalLibraryDependency)dependency).setJavadoc(javadocResult.getFile()); + } + } + } + + if (first) { + dependency.getDependencies().addAll( + transform(componentResult.getDependencies()) + ); + first = false; + } + + dependencies.add(dependency); + resolvedDepsFiles.add(artifact.getFile()); + } + } + } + } + + if (dependencyResult instanceof UnresolvedDependencyResult) { + ComponentSelector attempted = ((UnresolvedDependencyResult)dependencyResult).getAttempted(); + if (attempted instanceof ModuleComponentSelector) { + final ModuleComponentSelector attemptedMCSelector = (ModuleComponentSelector)attempted; + final DefaultUnresolvedExternalDependency dependency = new DefaultUnresolvedExternalDependency(); + dependency.setName(attemptedMCSelector.getModule()); + dependency.setGroup(attemptedMCSelector.getGroup()); + dependency.setVersion(attemptedMCSelector.getVersion()); + dependency.setScope(scope); + dependency.setFailureMessage(((UnresolvedDependencyResult)dependencyResult).getFailure().getMessage()); + + dependencies.add(dependency); + } + } + } + } + + return dependencies; + } + } + + private static boolean isProjectDependencyArtifact(ResolvedArtifact artifact) { + return isDependencySubstitutionsSupported && artifact.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier; + } + + private static MyModuleIdentifier toMyModuleIdentifier(ModuleVersionIdentifier id) { + return new MyModuleIdentifier(id.getName(), id.getGroup()); + } + + private static MyModuleIdentifier toMyModuleIdentifier(String name, String group) { + return new MyModuleIdentifier(name, group); + } + + static class MyModuleIdentifier { + String name; + String group; + + public MyModuleIdentifier(String name, String group) { + this.name = name; + this.group = group; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MyModuleIdentifier that = (MyModuleIdentifier)o; + + if (name != null ? !name.equals(that.name) : that.name != null) return false; + if (group != null ? !group.equals(that.group) : that.group != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (group != null ? group.hashCode() : 0); + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return group + ":" + name; + } + } +}