LocalizeDependenciesTask.groovy revision 24bec1cc542de69e0e21cc774469f20d34811ad9
1/*
2 * Copyright (C) 2015 The Android Open Source Project
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
17package android.databinding
18
19import groovy.io.FileType
20import org.apache.maven.repository.internal.MavenRepositorySystemUtils
21import org.eclipse.aether.DefaultRepositorySystemSession
22import org.eclipse.aether.RepositorySystem
23import org.eclipse.aether.RepositorySystemSession
24import org.eclipse.aether.artifact.Artifact
25import org.eclipse.aether.artifact.DefaultArtifact
26import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory
27import org.eclipse.aether.graph.Dependency
28import org.eclipse.aether.impl.DefaultServiceLocator
29import org.eclipse.aether.repository.LocalRepository
30import org.eclipse.aether.repository.RemoteRepository
31import org.eclipse.aether.resolution.ArtifactDescriptorRequest
32import org.eclipse.aether.resolution.ArtifactDescriptorResult
33import org.eclipse.aether.resolution.ArtifactRequest
34import org.eclipse.aether.spi.connector.RepositoryConnectorFactory
35import org.eclipse.aether.spi.connector.transport.TransporterFactory
36import org.eclipse.aether.transport.file.FileTransporterFactory
37import org.eclipse.aether.transport.http.HttpTransporterFactory
38import org.gradle.api.DefaultTask
39import org.gradle.api.artifacts.Configuration
40import org.gradle.api.artifacts.ModuleVersionIdentifier
41import org.gradle.api.tasks.TaskAction
42
43class LocalizeDependenciesTask extends DefaultTask {
44
45    private Set<String> ids = new HashSet<>();
46
47    private Set<String> fetchTestDependencies = new HashSet<>();
48
49    List<Artifact> artifactsToResolve = new LinkedList<>();
50
51    Set<String> resolvedArtifacts = new HashSet<>();
52
53    Set<String> failed = new HashSet<>();
54
55    HashMap<String, Object> licenses = new HashMap<>();
56
57    Set<String> missingLicenses = new HashSet<>();
58
59    File localRepoDir;
60
61    @TaskAction
62    doIt() {
63        println(ids)
64        LocalizePluginExtension extension = project.extensions.
65                getByName(MavenDependencyCollectorPlugin.EXTENSION_NAME)
66        if (extension.localRepoDir == null || extension.otherRepoDirs == null) {
67            throw new IllegalArgumentException("you must configure " +
68                    "${MavenDependencyCollectorPlugin.EXTENSION_NAME} with localRepoDir and" +
69                    " otherRepoDirs")
70        }
71        localRepoDir = extension.localRepoDir
72        downloadAll(extension.localRepoDir, extension.otherRepoDirs)
73
74        if (!missingLicenses.isEmpty()) {
75            throw new RuntimeException("Missing licenses for $missingLicenses")
76        }
77        println("List of new licenses:")
78        println(ExportLicensesTask.buildNotice(licenses))
79    }
80
81    public void add(MavenDependencyCollectorTask task, ModuleVersionIdentifier id, Configuration conf) {
82        def key = toStringIdentifier(id)
83        ids.add(key)
84        println("adding $key in $conf by $task")
85    }
86
87    public static String toStringIdentifier(ModuleVersionIdentifier id) {
88        return id.group + ":" + id.name + ":" + id.version;
89    }
90
91    private static String artifactKey(Artifact artifact) {
92        return artifact.groupId + ":" + artifact.artifactId + ":" + artifact.version;
93    }
94
95    public downloadAll(File localRepoDir, List<String> otherRepoDirs) {
96        println("downloading all dependencies to $localRepoDir")
97        def mavenCentral = new RemoteRepository.Builder("central", "default",
98                "http://central.maven.org/maven2/").build();
99        def system = newRepositorySystem()
100        localRepoDir = localRepoDir.canonicalFile
101        List<File> otherRepos = new ArrayList<>()
102        otherRepoDirs.each {
103            def repo = new File(it).getCanonicalFile()
104            if (repo.exists() && !repo.equals(localRepoDir)) {
105                otherRepos.add(repo)
106            }
107        }
108        def session = newRepositorySystemSession(system, localRepoDir)
109        ids.each {
110            def artifact = new DefaultArtifact(it)
111            artifactsToResolve.add(artifact)
112        }
113
114        while (!artifactsToResolve.isEmpty()) {
115            println("remaining artifacts to resolve ${artifactsToResolve.size()}")
116            Artifact artifact = artifactsToResolve.remove(0)
117            println("    handling artifact ${artifact.getArtifactId()}")
118            if (shouldSkip(artifact, otherRepos)) {
119                println("skipping $artifact")
120                continue
121            }
122            resolveArtifactWithDependencies(system, session, Arrays.asList(mavenCentral), artifact);
123        }
124    }
125
126    public static boolean shouldSkip(Artifact artifact, List<File> otherRepos) {
127        if (artifact.groupId.startsWith('com.android.databinding') ||
128                artifact.groupId.startsWith('com.android.support') ||
129                artifact.groupId.equals("jdk")){
130            return true
131        }
132        String targetPath = artifact.groupId.replaceAll("\\.", "/") + "/" + artifact.artifactId +
133                "/" + artifact.version
134        for (File repo : otherRepos) {
135            File f = new File(repo, targetPath)
136            if (f.exists()) {
137                println("skipping ${artifact} because it exists in $repo")
138                return true
139            }
140        }
141        return false
142    }
143
144    def boolean isInGit(File file) {
145        if (!file.getCanonicalPath().startsWith(localRepoDir.getCanonicalPath())) {
146            println("$file is in another git repo, ignore for license")
147            return false
148        }
149        def gitSt = ["git", "status", "--porcelain", file.getCanonicalPath()].
150                execute([], localRepoDir)
151        gitSt.waitFor()
152        if (gitSt.exitValue() != 0) {
153            throw new RuntimeException("unable to get git status for $file. ${gitSt.err.text}")
154        }
155        return gitSt.text.trim().isEmpty()
156    }
157
158    public void resolveArtifactWithDependencies(RepositorySystem system,
159            RepositorySystemSession session, List<RemoteRepository> remoteRepositories,
160            Artifact artifact) {
161        def key = artifactKey(artifact)
162        if (resolvedArtifacts.contains(key) || failed.contains(key)) {
163            return
164        }
165        resolvedArtifacts.add(key)
166        ArtifactRequest artifactRequest = new ArtifactRequest();
167        artifactRequest.setArtifact(artifact);
168        artifactRequest.setRepositories(remoteRepositories);
169        def resolved;
170        try {
171            resolved = system.resolveArtifact(session, artifactRequest);
172        } catch (Throwable ignored) {
173            println("cannot find $key, skipping")
174            failed.add(key)
175            return
176        }
177        def alreadyInGit = isInGit(resolved.artifact.file)
178        println("         |-> resolved ${resolved.artifact.file}. Already in git? $alreadyInGit")
179
180
181
182        if (!alreadyInGit) {
183            def license = ExportLicensesTask.findLicenseFor(resolved.artifact.artifactId)
184            if (license == null) {
185                missingLicenses.add(artifactKey(artifact))
186            } else {
187                licenses.put(resolved.artifact.artifactId, license)
188            }
189        }
190
191        ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
192        descriptorRequest.setArtifact(artifact);
193        descriptorRequest.setRepositories(remoteRepositories);
194
195        ArtifactDescriptorResult descriptorResult = system.
196                readArtifactDescriptor(session, descriptorRequest);
197        for (Dependency dependency : descriptorResult.getDependencies()) {
198            println("dependency $dependency for $artifact . scope: ${dependency.scope}")
199            if ("provided".equals(dependency.scope)) {
200                println("skipping $dependency because provided")
201                continue
202            }
203            if ("optional".equals(dependency.scope)) {
204                println("skipping $dependency because optional")
205                continue
206            }
207            if ("test".equals(dependency.scope)) {
208                if (fetchTestDependencies.contains(key)) {
209                    println("${dependency} is test scope but including because $key is in direct dependencies")
210                } else {
211                    println("skipping $dependency because test and not first level dependency")
212                    continue
213                }
214            }
215
216
217            def dependencyKey = artifactKey(dependency.artifact)
218            if (resolvedArtifacts.contains(dependencyKey)) {
219                println("skipping $dependency because is already resolved as ${dependencyKey}")
220                continue
221            }
222            println("adding to the list ${dependency.artifact}")
223            artifactsToResolve.add(dependency.artifact)
224        }
225        File unwanted = new File(resolved.artifact.file.getParentFile(), "_remote.repositories")
226        if (unwanted.exists()) {
227            unwanted.delete()
228        }
229    }
230
231    public static DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system,
232            File localRepoDir) {
233        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
234        LocalRepository localRepo = new LocalRepository(localRepoDir);
235        session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
236        return session;
237    }
238
239    public static RepositorySystem newRepositorySystem() {
240        DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
241        locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
242        locator.addService(TransporterFactory.class, FileTransporterFactory.class);
243        locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
244
245        return locator.getService(RepositorySystem.class);
246    }
247}
248