IDEA-155548 gradle: classpath does not include generated resources
[idea/community.git] / plugins / gradle / tooling-extension-impl / src / org / jetbrains / plugins / gradle / tooling / util / DependencyResolverImpl.groovy
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 package org.jetbrains.plugins.gradle.tooling.util
19
20 import com.google.common.collect.ArrayListMultimap
21 import com.google.common.collect.Lists
22 import com.google.common.collect.Multimap
23 import org.gradle.api.Project
24 import org.gradle.api.artifacts.Configuration
25 import org.gradle.api.artifacts.Dependency
26 import org.gradle.api.artifacts.ModuleVersionIdentifier
27 import org.gradle.api.artifacts.ProjectDependency
28 import org.gradle.api.artifacts.ResolvedArtifact
29 import org.gradle.api.artifacts.SelfResolvingDependency
30 import org.gradle.api.artifacts.component.ComponentIdentifier
31 import org.gradle.api.artifacts.component.ModuleComponentIdentifier
32 import org.gradle.api.artifacts.component.ModuleComponentSelector
33 import org.gradle.api.artifacts.component.ProjectComponentIdentifier
34 import org.gradle.api.artifacts.component.ProjectComponentSelector
35 import org.gradle.api.artifacts.result.*
36 import org.gradle.api.plugins.WarPlugin
37 import org.gradle.api.specs.Specs
38 import org.gradle.api.tasks.SourceSet
39 import org.gradle.api.tasks.SourceSetContainer
40 import org.gradle.api.tasks.SourceSetOutput
41 import org.gradle.api.tasks.bundling.AbstractArchiveTask
42 import org.gradle.api.tasks.compile.AbstractCompile
43 import org.gradle.language.base.artifact.SourcesArtifact
44 import org.gradle.language.java.artifact.JavadocArtifact
45 import org.gradle.plugins.ide.idea.IdeaPlugin
46 import org.gradle.util.GradleVersion
47 import org.jetbrains.annotations.NotNull
48 import org.jetbrains.annotations.Nullable
49 import org.jetbrains.plugins.gradle.model.*
50
51 import java.util.regex.Matcher
52 import java.util.regex.Pattern
53
54 /**
55  * @author Vladislav.Soroka
56  * @since 8/19/2015
57  */
58 class DependencyResolverImpl implements DependencyResolver {
59
60   private static isArtifactResolutionQuerySupported = GradleVersion.current() >= GradleVersion.version("2.0")
61   private static isDependencySubstitutionsSupported = GradleVersion.current() >= GradleVersion.version("2.5")
62
63   @NotNull
64   private final Project myProject
65   private final boolean myIsPreview
66   private final boolean myDownloadJavadoc
67   private final boolean myDownloadSources
68   private final SourceSetCachedFinder mySourceSetFinder
69
70   @SuppressWarnings("GroovyUnusedDeclaration")
71   DependencyResolverImpl(@NotNull Project project, boolean isPreview) {
72     myProject = project
73     myIsPreview = isPreview
74     myDownloadJavadoc = false
75     myDownloadSources = false
76     mySourceSetFinder = new SourceSetCachedFinder(project)
77   }
78
79   DependencyResolverImpl(
80     @NotNull Project project,
81     boolean isPreview,
82     boolean downloadJavadoc,
83     boolean downloadSources,
84     SourceSetCachedFinder sourceSetFinder) {
85     myProject = project
86     myIsPreview = isPreview
87     myDownloadJavadoc = downloadJavadoc
88     myDownloadSources = downloadSources
89     mySourceSetFinder = sourceSetFinder
90   }
91
92   @Override
93   Collection<ExternalDependency> resolveDependencies(@Nullable String configurationName) {
94     return resolveDependencies(configurationName, null)
95   }
96
97   Collection<ExternalDependency> resolveDependencies(@Nullable String configurationName, @Nullable String scope) {
98     if (configurationName == null) return Collections.emptyList()
99     def (result, resolvedFileDependencies) = resolveDependencies(myProject.configurations.findByName(configurationName), scope)
100     return result
101   }
102
103   @Override
104   Collection<ExternalDependency> resolveDependencies(@Nullable Configuration configuration) {
105     def (result, resolvedFileDependencies) = resolveDependencies(configuration, null)
106     return result
107   }
108
109   def resolveDependencies(@Nullable Configuration configuration, @Nullable String scope) {
110     if (configuration == null) return [Collections.emptyList(), Collections.emptyList()]
111     if (configuration.allDependencies.isEmpty()) return [Collections.emptyList(), Collections.emptyList()]
112
113     final Collection<ExternalDependency> result = new LinkedHashSet<>()
114
115     def resolvedFileDependencies = []
116     if (!myIsPreview && isArtifactResolutionQuerySupported) {
117       def jvmLibrary = null
118       try {
119         jvmLibrary = Class.forName('org.gradle.jvm.JvmLibrary')
120       }
121       catch (ClassNotFoundException ignored) {
122       }
123       if (jvmLibrary == null) {
124         try {
125           jvmLibrary = Class.forName('org.gradle.runtime.jvm.JvmLibrary')
126         }
127         catch (ClassNotFoundException ignored) {
128         }
129       }
130       if (jvmLibrary != null) {
131         Class[] artifactTypes = ([myDownloadSources?SourcesArtifact:null, myDownloadJavadoc?JavadocArtifact:null] - null) as Class[];
132         Set<ResolvedArtifact> resolvedArtifacts = configuration.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL)
133
134         Multimap<ModuleVersionIdentifier, ResolvedArtifact> artifactMap = ArrayListMultimap.create()
135         resolvedArtifacts.each { artifactMap.put(it.moduleVersion.id, it) }
136         //noinspection GroovyAssignabilityCheck
137         Set<ComponentArtifactsResult> componentResults = myProject.dependencies.createArtifactResolutionQuery()
138           .forComponents(resolvedArtifacts.collect { toComponentIdentifier(it.moduleVersion.id) })
139           .withArtifacts(jvmLibrary, artifactTypes)
140           .execute()
141           .getResolvedComponents()
142
143         Map<ComponentIdentifier, ComponentArtifactsResult> componentResultsMap = [:];
144         componentResults.each { componentResultsMap.put(it.id, it) }
145
146         Multimap<ModuleComponentIdentifier, ProjectDependency> configurationProjectDependencies = ArrayListMultimap.create()
147         configuration.incoming.dependencies.findAll { it instanceof ProjectDependency }.each {
148           configurationProjectDependencies.put(toComponentIdentifier(it.group, it.name, it.version), it as ProjectDependency)
149         }
150
151         ResolutionResult resolutionResult = configuration.incoming.resolutionResult
152         if(!configuration.resolvedConfiguration.hasError()) {
153           def fileDeps = new LinkedHashSet<File>(configuration.incoming.files.files);
154           artifactMap.values().each {
155             fileDeps.remove(it.file)
156           }
157           fileDeps.each {
158             def fileCollectionDependency = new DefaultFileCollectionDependency([it])
159             fileCollectionDependency.scope = scope
160             result.add(fileCollectionDependency)
161           }
162         }
163
164         def dependencyResultsTransformer = new DependencyResultsTransformer(artifactMap, componentResultsMap, configurationProjectDependencies, scope)
165         result.addAll(dependencyResultsTransformer.transform(resolutionResult.root.dependencies))
166
167         resolvedFileDependencies.addAll(dependencyResultsTransformer.resolvedDepsFiles)
168       }
169     }
170
171     if (myIsPreview || !isArtifactResolutionQuerySupported) {
172       def projectDependencies = findDependencies(configuration, configuration.allDependencies, scope)
173       result.addAll(projectDependencies);
174     }
175     def fileDependencies = findAllFileDependencies(configuration.allDependencies, scope)
176     result.addAll(fileDependencies - resolvedFileDependencies)
177
178     return [new ArrayList(result), resolvedFileDependencies]
179   }
180
181   @Override
182   Collection<ExternalDependency> resolveDependencies(@NotNull SourceSet sourceSet) {
183     Collection<ExternalDependency> result = new ArrayList<>()
184
185     // resolve compile dependencies
186     def compileConfigurationName = sourceSet.compileConfigurationName
187     def compileClasspathConfiguration = myProject.configurations.findByName(compileConfigurationName + 'Classpath')
188     def originCompileConfiguration = myProject.configurations.findByName(compileConfigurationName)
189     def compileConfiguration = compileClasspathConfiguration ?: originCompileConfiguration
190
191     def compileScope = 'COMPILE'
192     def (compileDependencies, resolvedCompileFileDependencies) = resolveDependencies(compileConfiguration, compileScope)
193     // resolve runtime dependencies
194     def runtimeConfigurationName = sourceSet.runtimeConfigurationName
195     def runtimeConfiguration = myProject.configurations.findByName(runtimeConfigurationName)
196
197     def runtimeScope = 'RUNTIME'
198     def (runtimeDependencies, resolvedRuntimeFileDependencies) = resolveDependencies(runtimeConfiguration, runtimeScope)
199
200     def providedScope = 'PROVIDED'
201
202     Multimap<Object, ExternalDependency> resolvedMap = ArrayListMultimap.create()
203
204     boolean checkCompileOnlyDeps = compileClasspathConfiguration && !originCompileConfiguration.resolvedConfiguration.hasError()
205     new DependencyTraverser(compileDependencies).each {
206       def resolvedObj = resolve(it)
207       resolvedMap.put(resolvedObj, it)
208
209       if (checkCompileOnlyDeps &&
210           (resolvedObj instanceof Collection ? !originCompileConfiguration.containsAll(((Collection)resolvedObj).toArray()) :
211            !originCompileConfiguration.contains(resolvedObj))) {
212         ((AbstractExternalDependency)it).scope = providedScope
213       }
214     }
215
216     new DependencyTraverser(runtimeDependencies).each {
217       Collection<ExternalDependency> dependencies = resolvedMap.get(resolve(it));
218       if (dependencies && !dependencies.isEmpty() && it.dependencies.isEmpty()) {
219         runtimeDependencies.remove(it)
220         ((AbstractExternalDependency)it).scope = dependencies.first().scope
221       }
222       else {
223         resolvedMap.put(resolve(it), it)
224       }
225     }
226
227     result.addAll(compileDependencies)
228     result.addAll(runtimeDependencies)
229     result.unique()
230
231     // merge file dependencies
232     def jvmLanguages = ['Java', 'Groovy', 'Scala']
233     def sourceSetCompileTaskPrefix = sourceSet.name == 'main' ? '' : sourceSet.name
234     def compileTasks = jvmLanguages.collect { 'compile' + sourceSetCompileTaskPrefix.capitalize() + it }
235
236     Map<File, Integer> compileClasspathOrder = new LinkedHashMap()
237     Set<File> compileClasspathFiles = new LinkedHashSet<>()
238
239     compileTasks.each {
240       def compileTask = myProject.tasks.findByName(it)
241       if (compileTask instanceof AbstractCompile) {
242         try {
243           def files = new ArrayList<>(compileTask.classpath.files)
244           files.removeAll(compileClasspathFiles)
245           compileClasspathFiles.addAll(files)
246         }
247         catch (ignore) {
248         }
249       }
250     }
251
252     try {
253       compileClasspathFiles = compileClasspathFiles.isEmpty() ? sourceSet.compileClasspath.files : compileClasspathFiles
254     }
255     catch (ignore) {
256     }
257     int order = 0;
258     for (File file : compileClasspathFiles) {
259       compileClasspathOrder.put(file, order++);
260     }
261     Map<File, Integer> runtimeClasspathOrder = new LinkedHashMap()
262     order = 0;
263     Set<File> runtimeClasspathFiles = new LinkedHashSet<File>()
264     try {
265       def files = sourceSet.runtimeClasspath.files
266       for (File file : files) {
267         runtimeClasspathOrder.put(file, order++);
268       }
269       runtimeClasspathFiles.addAll(files)
270     }
271     catch (ignore) {
272     }
273
274     runtimeClasspathFiles -= compileClasspathFiles
275     runtimeClasspathFiles -= sourceSet.output.files
276     compileClasspathFiles -= sourceSet.output.files
277
278     Multimap<String, File> resolvedDependenciesMap = ArrayListMultimap.create()
279     resolvedDependenciesMap.putAll(compileScope, resolvedCompileFileDependencies)
280     resolvedDependenciesMap.putAll(runtimeScope, resolvedRuntimeFileDependencies)
281     Project rootProject = myProject.rootProject
282
283     new DependencyTraverser(result).each {
284       def dependency = it
285       def scope = dependency.scope
286       order = -1;
287       if (dependency instanceof ExternalProjectDependency) {
288         ExternalProjectDependency projectDependency = dependency
289         def project = rootProject.findProject(projectDependency.projectPath)
290         def configuration = project?.configurations?.findByName(projectDependency.configurationName)
291         configuration?.allArtifacts?.files?.files?.each {
292           resolvedDependenciesMap.put(scope, it)
293           def classpathOrderMap = scope == compileScope ? compileClasspathOrder :
294                                   scope == runtimeScope ? runtimeClasspathOrder : null
295           if (classpathOrderMap) {
296             def fileOrder = classpathOrderMap.get(it)
297             if (fileOrder != null && (order == -1 || fileOrder < order)) {
298               order = fileOrder
299             }
300           }
301         }
302
303         //noinspection GrUnresolvedAccess
304         if (project.hasProperty("sourceSets") && (project.sourceSets instanceof SourceSetContainer) && project.sourceSets.main) {
305           //noinspection GrUnresolvedAccess
306           addSourceSetOutputDirsAsSingleEntryLibraries(result, project.sourceSets.main, runtimeClasspathOrder, scope)
307         }
308       }
309       else if (dependency instanceof ExternalLibraryDependency) {
310         resolvedDependenciesMap.put(scope, dependency.file)
311         def classpathOrderMap = scope == compileScope ? compileClasspathOrder :
312                                 scope == runtimeScope ? runtimeClasspathOrder : null
313         if (classpathOrderMap) {
314           def fileOrder = classpathOrderMap.get(dependency.file)
315           order = fileOrder != null ? fileOrder : -1
316         }
317       }
318       else if (dependency instanceof FileCollectionDependency) {
319         for (File file : dependency.files) {
320           resolvedDependenciesMap.put(scope, file)
321           def classpathOrderMap = scope == compileScope ? compileClasspathOrder :
322                                   scope == runtimeScope ? runtimeClasspathOrder : null
323           if (classpathOrderMap) {
324             def fileOrder = classpathOrderMap.get(file)
325             if (fileOrder != null && (order == -1 || fileOrder < order)) {
326               order = fileOrder
327             }
328             if (order == 0) break
329           }
330         }
331       }
332
333       if (dependency instanceof AbstractExternalDependency) {
334         dependency.classpathOrder = order
335       }
336     }
337
338     compileClasspathFiles.removeAll(resolvedDependenciesMap.get(compileScope))
339     compileClasspathFiles.removeAll(resolvedDependenciesMap.get(providedScope))
340     runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(runtimeScope))
341     runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(compileScope))
342     runtimeClasspathFiles.removeAll(resolvedDependenciesMap.get(providedScope))
343
344     Collection<ExternalDependency> fileDependencies = new ArrayList<>()
345     mapFileDependencies(runtimeClasspathFiles, runtimeScope, fileDependencies)
346     mapFileDependencies(compileClasspathFiles, compileScope, fileDependencies)
347
348     fileDependencies.each {
349       def dependency = it
350       def scope = dependency.scope
351       order = -1;
352       if (dependency instanceof ExternalLibraryDependency) {
353         def classpathOrderMap = scope == compileScope ? compileClasspathOrder :
354                                 scope == runtimeScope ? runtimeClasspathOrder : null
355         if (classpathOrderMap) {
356           def fileOrder = classpathOrderMap.get(dependency.file)
357           order = fileOrder != null ? fileOrder : -1
358         }
359       }
360       if (dependency instanceof AbstractExternalDependency) {
361         dependency.classpathOrder = order
362       }
363     }
364     result.addAll(fileDependencies)
365
366     if (!compileClasspathFiles.isEmpty()) {
367       final compileClasspathFilesDependency = new DefaultFileCollectionDependency(compileClasspathFiles)
368       compileClasspathFilesDependency.scope = compileScope
369
370       order = -1;
371       for (File file : compileClasspathFiles) {
372         def fileOrder = compileClasspathOrder.get(file)
373         if (fileOrder != null && (order == -1 || fileOrder < order)) {
374           order = fileOrder
375         }
376         if (order == 0) break
377       }
378
379       if (order != -1) {
380         compileClasspathFilesDependency.classpathOrder = order
381       }
382       result.add(compileClasspathFilesDependency)
383       for (File file : compileClasspathFiles) {
384         def outputDirSourceSet = mySourceSetFinder.findByArtifact(file.path)
385         if(outputDirSourceSet) {
386           addSourceSetOutputDirsAsSingleEntryLibraries(result, outputDirSourceSet, compileClasspathOrder, compileScope)
387         }
388       }
389     }
390
391     if (!runtimeClasspathFiles.isEmpty()) {
392       final runtimeClasspathFilesDependency = new DefaultFileCollectionDependency(runtimeClasspathFiles)
393       runtimeClasspathFilesDependency.scope = runtimeScope
394
395       order = -1;
396       for (File file : runtimeClasspathFiles) {
397         def fileOrder = runtimeClasspathOrder.get(file)
398         if (fileOrder != null && (order == -1 || fileOrder < order)) {
399           order = fileOrder
400         }
401         if (order == 0) break
402       }
403
404       runtimeClasspathFilesDependency.classpathOrder = order
405       result.add(runtimeClasspathFilesDependency)
406
407       for (File file : runtimeClasspathFiles) {
408         def outputDirSourceSet = mySourceSetFinder.findByArtifact(file.path)
409         if(outputDirSourceSet) {
410           addSourceSetOutputDirsAsSingleEntryLibraries(result, outputDirSourceSet, runtimeClasspathOrder, runtimeScope)
411         }
412       }
413     }
414
415     addSourceSetOutputDirsAsSingleEntryLibraries(result, sourceSet, runtimeClasspathOrder, runtimeScope)
416
417     // handle provided dependencies
418     def providedConfigurations = new LinkedHashSet<Configuration>()
419     resolvedMap = ArrayListMultimap.create()
420     new DependencyTraverser(result).each { resolvedMap.put(resolve(it), it) }
421     final IdeaPlugin ideaPlugin = myProject.getPlugins().findPlugin(IdeaPlugin.class);
422     if (ideaPlugin) {
423       def scopes = ideaPlugin.model.module.scopes
424       def providedPlusScopes = scopes.get(providedScope)
425       if (providedPlusScopes && providedPlusScopes.get("plus")) {
426         providedConfigurations.addAll(providedPlusScopes.get("plus"))
427       }
428     }
429     if (sourceSet.name == 'main' && myProject.plugins.findPlugin(WarPlugin)) {
430       providedConfigurations.add(myProject.configurations.findByName('providedCompile'))
431       providedConfigurations.add(myProject.configurations.findByName('providedRuntime'))
432     }
433     providedConfigurations.each {
434       def (providedDependencies, resolvedProvidedFileDependencies) = resolveDependencies(it, providedScope)
435       new DependencyTraverser(providedDependencies).each {
436         Collection<ExternalDependency> dependencies = resolvedMap.get(resolve(it));
437         if (!dependencies.isEmpty()) {
438           if (it.dependencies.isEmpty()) {
439             providedDependencies.remove(it)
440           }
441           dependencies.each {
442             ((AbstractExternalDependency)it).scope = providedScope
443           }
444         }
445         else {
446           resolvedMap.put(resolve(it), it)
447         }
448       }
449       result.addAll(providedDependencies)
450     }
451
452     return removeDuplicates(resolvedMap, result)
453   }
454
455   private static List<ExternalDependency> removeDuplicates(
456     ArrayListMultimap<Object, ExternalDependency> resolvedMap,  List<ExternalDependency> result) {
457     resolvedMap.asMap().values().each {
458       def toRemove = []
459       def isCompileScope = false
460       def isProvidedScope = false
461       it.each {
462         if (it.dependencies.isEmpty()) {
463           toRemove.add(it)
464           if(it.scope == 'COMPILE') isCompileScope = true
465           else if(it.scope == 'PROVIDED') isProvidedScope = true
466         }
467       }
468       if (toRemove.size() != it.size()) {
469         result.removeAll(toRemove)
470       }
471       else if (toRemove.size() > 1) {
472         toRemove.drop(1)
473         result.removeAll(toRemove)
474       }
475       if(!toRemove.isEmpty()) {
476         def retained = it - toRemove
477         if(!retained.isEmpty()) {
478           def retainedDependency = retained.first() as AbstractExternalDependency
479           if(retainedDependency instanceof AbstractExternalDependency && retainedDependency.scope != 'COMPILE') {
480             if(isCompileScope) retainedDependency.scope = 'COMPILE'
481             else if(isProvidedScope) retainedDependency.scope = 'PROVIDED'
482           }
483         }
484       }
485     }
486
487     return result.unique()
488   }
489
490   static def resolve(ExternalDependency dependency) {
491     if (dependency instanceof ExternalLibraryDependency) {
492       return dependency.file
493     } else if (dependency instanceof FileCollectionDependency) {
494       return dependency.files
495     } else if (dependency instanceof ExternalMultiLibraryDependency) {
496       return dependency.files
497     } else if (dependency instanceof ExternalProjectDependency) {
498       return dependency.projectDependencyArtifacts
499     }
500     null
501   }
502
503   private static void addSourceSetOutputDirsAsSingleEntryLibraries(
504     Collection<ExternalDependency> dependencies,
505     SourceSet sourceSet,
506     Map<File, Integer> classpathOrder,
507     String scope) {
508     Set<File> runtimeOutputDirs = sourceSet.output.dirs.files
509     runtimeOutputDirs.each {
510       final runtimeOutputDirsDependency = new DefaultFileCollectionDependency([it])
511       runtimeOutputDirsDependency.scope = scope
512       def fileOrder = classpathOrder.get(it)
513       runtimeOutputDirsDependency.classpathOrder = fileOrder != null ? fileOrder : -1
514       dependencies.add(runtimeOutputDirsDependency)
515     }
516   }
517
518
519   @Nullable
520   ExternalLibraryDependency resolveLibraryByPath(File file, String scope) {
521     File modules2Dir = new File(myProject.gradle.gradleUserHomeDir, "caches/modules-2/files-2.1");
522     return resolveLibraryByPath(file, modules2Dir, scope)
523   }
524
525   @Nullable
526   static ExternalLibraryDependency resolveLibraryByPath(File file, File modules2Dir, String scope) {
527     File sourcesFile = null;
528     def modules2Path = modules2Dir.canonicalPath
529     def filePath = file.canonicalPath
530     if (filePath.startsWith(modules2Path)) {
531       List<File> parents = new ArrayList<>()
532       File parent = file.parentFile;
533       while(parent && !parent.name.equals(modules2Dir.name)) {
534         parents.add(parent)
535         parent = parent.parentFile
536       }
537
538       def groupDir = parents.get(parents.size() - 1)
539       def artifactDir = parents.get(parents.size() - 2)
540       def versionDir = parents.get(parents.size() - 3)
541
542       def parentFile = versionDir
543       if (parentFile != null) {
544         def hashDirs = parentFile.listFiles()
545         if (hashDirs != null) {
546           for (File hashDir : hashDirs) {
547             def sourcesJars = hashDir.listFiles(new FilenameFilter() {
548               @Override
549               boolean accept(File dir, String name) {
550                 return name.endsWith("sources.jar")
551               }
552             })
553
554             if (sourcesJars != null && sourcesJars.length > 0) {
555               sourcesFile = sourcesJars[0];
556               break
557             }
558           }
559
560           def packaging = resolvePackagingType(file);
561           def classifier = resolveClassifier(artifactDir.name, versionDir.name, file);
562           return new DefaultExternalLibraryDependency(
563             name: artifactDir.name,
564             group: groupDir.name,
565             packaging: packaging,
566             classifier: classifier,
567             version: versionDir.name,
568             file: file,
569             source: sourcesFile,
570             scope: scope
571           )
572         }
573       }
574     }
575
576     null
577   }
578
579   def mapFileDependencies(Set<File> fileDependencies, String scope, Collection<ExternalDependency> dependencies) {
580     File modules2Dir = new File(myProject.gradle.gradleUserHomeDir, "caches/modules-2/files-2.1");
581     List toRemove = new ArrayList()
582     for (File file : fileDependencies) {
583       def libraryDependency = resolveLibraryByPath(file, modules2Dir, scope)
584       if (libraryDependency) {
585         dependencies.add(libraryDependency)
586         toRemove.add(file)
587       }
588       else {
589         //noinspection GrUnresolvedAccess
590         def name = file.name.lastIndexOf('.').with { it != -1 ? file.name[0..<it] : file.name }
591         def sourcesFile = new File(file.parentFile, name + '-sources.jar')
592         if (sourcesFile.exists()) {
593           libraryDependency = new DefaultExternalLibraryDependency(
594             file: file,
595             source: sourcesFile,
596             scope: scope
597           )
598           if (libraryDependency) {
599             dependencies.add(libraryDependency)
600             toRemove.add(file)
601           }
602         }
603       }
604     }
605
606     fileDependencies.removeAll(toRemove)
607   }
608
609   @Nullable
610   static String resolvePackagingType(File file) {
611     if (file == null) return 'jar'
612     def path = file.getPath()
613     int index = path.lastIndexOf('.');
614     if (index < 0) return 'jar';
615     return path.substring(index + 1)
616   }
617
618   @Nullable
619   static String resolveClassifier(String name, String version, File file) {
620     String libraryFileName = getNameWithoutExtension(file);
621     final String mavenLibraryFileName = "$name-$version";
622     if (!mavenLibraryFileName.equals(libraryFileName)) {
623       Matcher matcher = Pattern.compile("$name-$version-(.*)").matcher(libraryFileName);
624       if (matcher.matches()) {
625         return matcher.group(1);
626       }
627     }
628     return null
629   }
630
631   static String getNameWithoutExtension(File file) {
632     if (file == null) return null
633     def name = file.name
634     int i = name.lastIndexOf('.');
635     if (i != -1) {
636       name = name.substring(0, i);
637     }
638     return name;
639   }
640
641   private static toComponentIdentifier(ModuleVersionIdentifier id) {
642     return new ModuleComponentIdentifierImpl(id.getGroup(), id.getName(), id.getVersion());
643   }
644
645   private static toComponentIdentifier(@NotNull String group, @NotNull String module, @NotNull String version) {
646     return new ModuleComponentIdentifierImpl(group, module, version);
647   }
648
649   private static Set<ExternalDependency> findAllFileDependencies(
650     Collection<Dependency> dependencies, String scope) {
651     Set<ExternalDependency> result = new LinkedHashSet<>()
652
653     dependencies.each {
654       try {
655         if (it instanceof SelfResolvingDependency && !(it instanceof ProjectDependency)) {
656           def files = it.resolve()
657           if (files && !files.isEmpty()) {
658             final dependency = new DefaultFileCollectionDependency(files)
659             dependency.scope = scope
660             result.add(dependency)
661           }
662         }
663       }
664       catch (ignore) {
665       }
666     }
667
668     return result;
669   }
670
671   private Set<ExternalDependency> findDependencies(
672     Configuration configuration,
673     Collection<Dependency> dependencies,
674     String scope) {
675     Set<ExternalDependency> result = new LinkedHashSet<>()
676
677     Set<ResolvedArtifact> resolvedArtifacts = myIsPreview ? new HashSet<>() :
678                                               configuration.resolvedConfiguration.lenientConfiguration.getArtifacts(Specs.SATISFIES_ALL)
679
680     Multimap<MyModuleIdentifier, ResolvedArtifact> artifactMap = ArrayListMultimap.create()
681     resolvedArtifacts.each { artifactMap.put(toMyModuleIdentifier(it.moduleVersion.id), it) }
682
683     dependencies.each {
684       try {
685         if (it instanceof ProjectDependency) {
686           def project = it.getDependencyProject()
687           final projectDependency = new DefaultExternalProjectDependency(
688             name: project.name,
689             group: project.group,
690             version: project.version,
691             scope: scope,
692             projectPath: project.path,
693             configurationName: it.projectConfiguration.name
694           )
695           projectDependency.projectDependencyArtifacts = it.projectConfiguration.allArtifacts.files.files
696           result.add(projectDependency)
697         }
698         else if (it instanceof Dependency) {
699           def artifactsResult = artifactMap.get(toMyModuleIdentifier(it.name, it.group))
700           if (artifactsResult && !artifactsResult.isEmpty()) {
701             def artifact = artifactsResult.first()
702             def packaging = artifact.extension ?: 'jar'
703             def classifier = artifact.classifier
704             File sourcesFile = resolveLibraryByPath(artifact.file, scope)?.source;
705             def libraryDependency = new DefaultExternalLibraryDependency(
706               name: it.name,
707               group: it.group,
708               packaging: packaging,
709               classifier: classifier,
710               version: artifact.moduleVersion.id.version,
711               scope: scope,
712               file: artifact.file,
713               source: sourcesFile
714             )
715             result.add(libraryDependency)
716           }
717           else {
718             if (!(it instanceof SelfResolvingDependency) && !myIsPreview) {
719               final dependency = new DefaultUnresolvedExternalDependency(
720                 name: it.name,
721                 group: it.group,
722                 version: it.version,
723                 scope: scope,
724                 failureMessage: "Could not find " + it.group + ":" + it.name + ":" + it.version
725               )
726               result.add(dependency)
727             }
728           }
729         }
730       }
731       catch (ignore) {
732       }
733     }
734
735     return result;
736   }
737
738   static class DependencyResultsTransformer {
739     Collection<DependencyResult> handledDependencyResults
740     Multimap<ModuleVersionIdentifier, ResolvedArtifact> artifactMap
741     Map<ComponentIdentifier, ComponentArtifactsResult> componentResultsMap
742     Multimap<ModuleComponentIdentifier, ProjectDependency> configurationProjectDependencies
743     String scope
744     Set<File> resolvedDepsFiles = []
745
746     DependencyResultsTransformer(
747       Multimap<ModuleVersionIdentifier, ResolvedArtifact> artifactMap,
748       Map<ComponentIdentifier, ComponentArtifactsResult> componentResultsMap,
749       Multimap<ModuleComponentIdentifier, ProjectDependency> configurationProjectDependencies,
750       String scope) {
751       this.handledDependencyResults = Lists.newArrayList()
752       this.artifactMap = artifactMap
753       this.componentResultsMap = componentResultsMap
754       this.configurationProjectDependencies = configurationProjectDependencies
755       this.scope = scope
756     }
757
758     Set<ExternalDependency> transform(Collection<DependencyResult> dependencyResults) {
759
760       Set<ExternalDependency> dependencies = new LinkedHashSet<>()
761       dependencyResults.each { DependencyResult dependencyResult ->
762
763         // dependency cycles check
764         if (!handledDependencyResults.contains(dependencyResult)) {
765           handledDependencyResults.add(dependencyResult)
766
767           if (dependencyResult instanceof ResolvedDependencyResult) {
768             def componentResult = dependencyResult.selected
769             def componentSelector = dependencyResult.requested
770             def componentIdentifier = toComponentIdentifier(componentResult.moduleVersion)
771             def name = componentResult.moduleVersion.name
772             def group = componentResult.moduleVersion.group
773             def version = componentResult.moduleVersion.version
774             def selectionReason = componentResult.selectionReason.description
775             if (componentSelector instanceof ProjectComponentSelector) {
776               def projectDependencies = configurationProjectDependencies.get(componentIdentifier)
777               projectDependencies.each {
778                 if (it.projectConfiguration.name == Dependency.DEFAULT_CONFIGURATION) {
779                   final dependency = new DefaultExternalProjectDependency(
780                     name: name,
781                     group: group,
782                     version: version,
783                     scope: scope,
784                     selectionReason: selectionReason,
785                     projectPath: componentSelector.projectPath,
786                     configurationName: it.projectConfiguration.name
787                   )
788                   dependency.projectDependencyArtifacts = artifactMap.get(componentResult.moduleVersion).collect { it.file }
789                   dependency.projectDependencyArtifacts.each { resolvedDepsFiles.add(it) }
790
791                   if (componentResult != dependencyResult.from) {
792                     dependency.dependencies.addAll(
793                       transform(componentResult.dependencies)
794                     )
795                   }
796                   dependencies.add(dependency)
797                 }
798                 else {
799                   final dependency = new DefaultExternalProjectDependency(
800                     name: name,
801                     group: group,
802                     version: version,
803                     scope: scope,
804                     selectionReason: selectionReason,
805                     projectPath: componentSelector.projectPath,
806                     configurationName: it.projectConfiguration.name
807                   )
808                   dependency.projectDependencyArtifacts = artifactMap.get(componentResult.moduleVersion).collect { it.file }
809                   dependency.projectDependencyArtifacts.each { resolvedDepsFiles.add(it) }
810
811                   if (componentResult != dependencyResult.from) {
812                     dependency.dependencies.addAll(
813                       transform(componentResult.dependencies)
814                     )
815                   }
816                   dependencies.add(dependency)
817
818                   def files = []
819                   def artifacts = it.projectConfiguration.getArtifacts()
820                   if (artifacts && !artifacts.isEmpty()) {
821                     def artifact = artifacts.first()
822                     if (artifact.hasProperty("archiveTask") &&
823                         (artifact.archiveTask instanceof org.gradle.api.tasks.bundling.AbstractArchiveTask)) {
824                       def archiveTask = artifact.archiveTask as AbstractArchiveTask
825                       resolvedDepsFiles.add(new File(archiveTask.destinationDir, archiveTask.archiveName))
826
827                       def mainSpec = archiveTask.mainSpec
828                       def sourcePaths
829                       if (mainSpec.metaClass.respondsTo(mainSpec, 'getSourcePaths')) {
830                         sourcePaths = mainSpec.getSourcePaths()
831                       }
832                       else if (mainSpec.hasProperty('sourcePaths')) {
833                         sourcePaths = mainSpec.sourcePaths
834                       }
835                       if (sourcePaths) {
836                         (sourcePaths.flatten() as List).each { def path ->
837                           if (path instanceof String) {
838                             def file = new File(path)
839                             if (file.isAbsolute()) {
840                               files.add(file)
841                             }
842                           }
843                           else if (path instanceof SourceSetOutput) {
844                             files.addAll(path.files)
845                           }
846                         }
847                       }
848                     }
849                   }
850
851                   if(!files.isEmpty()) {
852                     final fileCollectionDependency = new DefaultFileCollectionDependency(files)
853                     fileCollectionDependency.scope = scope
854                     dependencies.add(fileCollectionDependency)
855                     resolvedDepsFiles.addAll(files)
856                   }
857                 }
858               }
859             }
860             if (componentSelector instanceof ModuleComponentSelector) {
861               def artifacts = artifactMap.get(componentResult.moduleVersion)
862               def artifact = artifacts?.find { true }
863
864               if (artifacts?.isEmpty()) {
865                 dependencies.addAll(
866                   transform(componentResult.dependencies)
867                 )
868               }
869               boolean first = true
870               artifacts?.each {
871                 artifact = it
872                 def packaging = it.extension ?: 'jar'
873                 def classifier = it.classifier
874                 final dependency
875                 if (isDependencySubstitutionsSupported && artifact.id.componentIdentifier instanceof ProjectComponentIdentifier) {
876                   def artifactComponentIdentifier = artifact.id.componentIdentifier as ProjectComponentIdentifier
877                   dependency = new DefaultExternalProjectDependency(
878                     name: name,
879                     group: group,
880                     version: version,
881                     scope: scope,
882                     selectionReason: selectionReason,
883                     projectPath: artifactComponentIdentifier.projectPath,
884                     configurationName: Dependency.DEFAULT_CONFIGURATION
885                   )
886                   dependency.projectDependencyArtifacts = artifactMap.get(componentResult.moduleVersion).collect { it.file }
887                   dependency.projectDependencyArtifacts.each { resolvedDepsFiles.add(it) }
888                 }
889                 else {
890                   dependency = new DefaultExternalLibraryDependency(
891                     name: name,
892                     group: group,
893                     packaging: packaging,
894                     classifier: classifier,
895                     version: version,
896                     scope: scope,
897                     selectionReason: selectionReason,
898                     file: artifact.file
899                   )
900
901                   def artifactsResult = componentResultsMap.get(componentIdentifier)
902                   if (artifactsResult) {
903                     def sourcesResult = artifactsResult.getArtifacts(SourcesArtifact)?.find { it instanceof ResolvedArtifactResult }
904                     if (sourcesResult) {
905                       dependency.setSource(((ResolvedArtifactResult)sourcesResult).getFile())
906                     }
907                     def javadocResult = artifactsResult.getArtifacts(JavadocArtifact)?.find { it instanceof ResolvedArtifactResult }
908                     if (javadocResult) {
909                       dependency.setJavadoc(((ResolvedArtifactResult)javadocResult).getFile())
910                     }
911                   }
912                 }
913                 if (first) {
914                   dependency.dependencies.addAll(
915                     transform(componentResult.dependencies)
916                   )
917                   first = false
918                 }
919
920                 dependencies.add(dependency)
921                 resolvedDepsFiles.add(artifact.file)
922               }
923             }
924           }
925
926           if (dependencyResult instanceof UnresolvedDependencyResult) {
927             def componentResult = dependencyResult.attempted
928             if (componentResult instanceof ModuleComponentSelector) {
929               final dependency = new DefaultUnresolvedExternalDependency(
930                 name: componentResult.module,
931                 group: componentResult.group,
932                 version: componentResult.version,
933                 scope: scope,
934                 failureMessage: dependencyResult.failure.message
935               )
936               dependencies.add(dependency)
937             }
938           }
939         }
940       }
941
942       return dependencies
943     }
944   }
945
946   private static toMyModuleIdentifier(ModuleVersionIdentifier id) {
947     return new MyModuleIdentifier(name: id.getName(), group: id.getGroup());
948   }
949
950   private static toMyModuleIdentifier(String name, String group) {
951     return new MyModuleIdentifier(name: name, group: group);
952   }
953
954   static class MyModuleIdentifier {
955     String name
956     String group
957
958     boolean equals(o) {
959       if (this.is(o)) return true
960       if (!(o instanceof MyModuleIdentifier)) return false
961
962       MyModuleIdentifier that = (MyModuleIdentifier)o
963
964       if (group != that.group) return false
965       if (name != that.name) return false
966
967       return true
968     }
969
970     int hashCode() {
971       int result = (group != null ? group.hashCode() : 0)
972       result = 31 * result + (name != null ? name.hashCode() : 0)
973       return result
974     }
975
976     @Override
977     String toString() {
978       return "$group:$name"
979     }
980   }
981 }