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