LocalizeDependenciesTask.groovy revision 0a39d327fc5dedb0a766f1ad44d7b1fc7048ef84
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