LocalizeDependenciesTask.groovy revision 9784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3
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