LocalizeDependenciesTask.groovy revision 499cacaab504a8166dfe44515e0242b9852d8673
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            return
71        }
72        localRepoDir = extension.localRepoDir
73        downloadAll(extension.localRepoDir, extension.otherRepoDirs)
74
75        if (!missingLicenses.isEmpty()) {
76            throw new RuntimeException("Missing licenses for $missingLicenses")
77        }
78        println("List of new licenses:")
79        println(ExportLicensesTask.buildNotice(licenses))
80    }
81
82    public void add(MavenDependencyCollectorTask task, ModuleVersionIdentifier id, Configuration conf) {
83        def key = toStringIdentifier(id)
84        ids.add(key)
85        println("adding $key in $conf by $task")
86    }
87
88    public static String toStringIdentifier(ModuleVersionIdentifier id) {
89        return id.group + ":" + id.name + ":" + id.version;
90    }
91
92    private static String artifactKey(Artifact artifact) {
93        return artifact.groupId + ":" + artifact.artifactId + ":" + artifact.version;
94    }
95
96    public downloadAll(File localRepoDir, List<String> otherRepoDirs) {
97        println("downloading all dependencies to $localRepoDir")
98        def mavenCentral = new RemoteRepository.Builder("central", "default",
99                "http://central.maven.org/maven2/").build();
100        def system = newRepositorySystem()
101        localRepoDir = localRepoDir.canonicalFile
102        List<File> otherRepos = new ArrayList<>()
103        otherRepoDirs.each {
104            def repo = new File(it).getCanonicalFile()
105            if (repo.exists() && !repo.equals(localRepoDir)) {
106                otherRepos.add(repo)
107            }
108        }
109        def session = newRepositorySystemSession(system, localRepoDir)
110        ids.each {
111            def artifact = new DefaultArtifact(it)
112            artifactsToResolve.add(artifact)
113        }
114
115        while (!artifactsToResolve.isEmpty()) {
116            println("remaining artifacts to resolve ${artifactsToResolve.size()}")
117            Artifact artifact = artifactsToResolve.remove(0)
118            println("    handling artifact ${artifact.getArtifactId()}")
119            if (shouldSkip(artifact, otherRepos)) {
120                println("skipping $artifact")
121                continue
122            }
123            resolveArtifactWithDependencies(system, session, Arrays.asList(mavenCentral), artifact);
124        }
125    }
126
127    public static boolean shouldSkip(Artifact artifact, List<File> otherRepos) {
128        if (artifact.groupId.startsWith('com.android.databinding') ||
129                artifact.groupId.startsWith('com.android.support') ||
130                artifact.groupId.equals("jdk")){
131            return true
132        }
133        String targetPath = artifact.groupId.replaceAll("\\.", "/") + "/" + artifact.artifactId +
134                "/" + artifact.version
135        for (File repo : otherRepos) {
136            File f = new File(repo, targetPath)
137            if (f.exists()) {
138                println("skipping ${artifact} because it exists in $repo")
139                return true
140            }
141        }
142        return false
143    }
144
145    def boolean isInGit(File file) {
146        if (!file.getCanonicalPath().startsWith(localRepoDir.getCanonicalPath())) {
147            println("$file is in another git repo, ignore for license")
148            return false
149        }
150        def gitSt = ["git", "status", "--porcelain", file.getCanonicalPath()].
151                execute([], localRepoDir)
152        gitSt.waitFor()
153        if (gitSt.exitValue() != 0) {
154            throw new RuntimeException("unable to get git status for $file. ${gitSt.err.text}")
155        }
156        return gitSt.text.trim().isEmpty()
157    }
158
159    public void resolveArtifactWithDependencies(RepositorySystem system,
160            RepositorySystemSession session, List<RemoteRepository> remoteRepositories,
161            Artifact artifact) {
162        def key = artifactKey(artifact)
163        if (resolvedArtifacts.contains(key) || failed.contains(key)) {
164            return
165        }
166        resolvedArtifacts.add(key)
167        ArtifactRequest artifactRequest = new ArtifactRequest();
168        artifactRequest.setArtifact(artifact);
169        artifactRequest.setRepositories(remoteRepositories);
170        def resolved;
171        try {
172            resolved = system.resolveArtifact(session, artifactRequest);
173        } catch (Throwable ignored) {
174            println("cannot find $key, skipping")
175            failed.add(key)
176            return
177        }
178        def alreadyInGit = isInGit(resolved.artifact.file)
179        println("         |-> resolved ${resolved.artifact.file}. Already in git? $alreadyInGit")
180
181
182
183        if (!alreadyInGit) {
184            def license = ExportLicensesTask.findLicenseFor(resolved.artifact.artifactId)
185            if (license == null) {
186                missingLicenses.add(artifactKey(artifact))
187            } else {
188                licenses.put(resolved.artifact.artifactId, license)
189            }
190        }
191
192        ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
193        descriptorRequest.setArtifact(artifact);
194        descriptorRequest.setRepositories(remoteRepositories);
195
196        ArtifactDescriptorResult descriptorResult = system.
197                readArtifactDescriptor(session, descriptorRequest);
198        for (Dependency dependency : descriptorResult.getDependencies()) {
199            println("dependency $dependency for $artifact . scope: ${dependency.scope}")
200            if ("provided".equals(dependency.scope)) {
201                println("skipping $dependency because provided")
202                continue
203            }
204            if ("optional".equals(dependency.scope)) {
205                println("skipping $dependency because optional")
206                continue
207            }
208            if ("test".equals(dependency.scope)) {
209                if (fetchTestDependencies.contains(key)) {
210                    println("${dependency} is test scope but including because $key is in direct dependencies")
211                } else {
212                    println("skipping $dependency because test and not first level dependency")
213                    continue
214                }
215            }
216
217
218            def dependencyKey = artifactKey(dependency.artifact)
219            if (resolvedArtifacts.contains(dependencyKey)) {
220                println("skipping $dependency because is already resolved as ${dependencyKey}")
221                continue
222            }
223            println("adding to the list ${dependency.artifact}")
224            artifactsToResolve.add(dependency.artifact)
225        }
226        File unwanted = new File(resolved.artifact.file.getParentFile(), "_remote.repositories")
227        if (unwanted.exists()) {
228            unwanted.delete()
229        }
230    }
231
232    public static DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system,
233            File localRepoDir) {
234        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
235        LocalRepository localRepo = new LocalRepository(localRepoDir);
236        session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
237        return session;
238    }
239
240    public static RepositorySystem newRepositorySystem() {
241        DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
242        locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
243        locator.addService(TransporterFactory.class, FileTransporterFactory.class);
244        locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
245
246        return locator.getService(RepositorySystem.class);
247    }
248}
249