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