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