From 4a7dc79c68f98aa17a8ba74b1d4788a86a38ec30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schalk=20W=2E=20Cronj=C3=A9?= Date: Wed, 8 Nov 2017 14:42:06 +0100 Subject: [PATCH] Simplified multi-project support (#25). - Added addAllProjects() and addProject(). - Method 'configurations' will detect a `Configuration` instance correctly. - method getConfigurations return an iterable `Configuration` collection rather than a `ConfigurationContainer`. --- .../ivypot/OfflineRepositorySync.groovy | 169 ++++++++++++++---- .../ivypot/internal/RepositoryTraits.groovy | 2 +- .../ivypot/OfflineRepositorySyncSpec.groovy | 61 +++++-- 3 files changed, 188 insertions(+), 44 deletions(-) diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy index 66e4395..9ac5117 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySync.groovy @@ -23,8 +23,8 @@ import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.artifacts.repositories.IvyArtifactRepository import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler @@ -36,16 +36,12 @@ import org.gradle.api.tasks.TaskAction import org.gradle.internal.FileUtils import org.gradle.internal.reflect.DirectInstantiator import org.gradle.internal.reflect.Instantiator -import org.gradle.util.CollectionUtils import org.ysb33r.gradle.ivypot.internal.BaseRepositoryFactory +import org.ysb33r.grolifant.api.StringUtils @CompileStatic class OfflineRepositorySync extends DefaultTask { - - private static final String LOCALREPONAME = '~~~local~~~repo~~~' - private static final String REMOTECHAINNAME = '~~~remote~~~resolvers~~~' - @CompileDynamic OfflineRepositorySync() { String ivyJar = findIvyJarPath(project) @@ -60,6 +56,10 @@ class OfflineRepositorySync extends DefaultTask { classpath : ivyJar repositories = createRepositoryHandler(project.gradle) + + inputs.properties.put('project configurations', { + this.projectConfigurations + }) } /** The pattern that will be used to write artifacts into the target repository @@ -74,6 +74,9 @@ class OfflineRepositorySync extends DefaultTask { @Input String repoIvyPattern = IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN + /** Include buildscript dependencies from the root project. + * + */ @Input boolean includeBuildScriptDependencies = false @@ -91,24 +94,20 @@ class OfflineRepositorySync extends DefaultTask { this.repoRoot = repo } + /** If no configurations were listed, returns all the configurations * * @return A project configuration container with all of the named configurations. Does not * return the {@code buildscript} configuration in here. The latter is made available directly to */ - @CompileDynamic - ConfigurationContainer getConfigurations() { - ConfigurationContainer cc - if(this.configurations) { - List names = CollectionUtils.stringize(this.configurations) - cc = project.configurations.matching { Configuration it -> - names.contains(it.name) - } as ConfigurationContainer - } else { - cc = project.configurations + Set getConfigurations() { + Set configurationSet= [] + projectConfigurations.collect { Project p, List configs -> + configurationSet.addAll(getConfigurationsFor(p,configs)) } - return cc + configurationSet.addAll(getConfigurationsFor(project,this.configurations)) + configurationSet } /** Clears the current list of configurations and assigns a new list @@ -128,6 +127,78 @@ class OfflineRepositorySync extends DefaultTask { this.configurations.addAll(names as List) } + /** Adds configurations from all projects except the current one + * + *

Any existing project configurations (except the current project) will be replaced. + */ + void addAllProjects() { + addConfigurationsRecursivelyFrom(project.rootProject) + } + + /** Adds all of the configurations for a project. + * + *

If the project has been added previously, the existing list of configurations will be + * overriden by this one. + * + * @param p Project for which all configuratiosn needs to be added. If it is the current project + * and exception will be thrown. + * @throw {@link CannotUseCurrentProjectException} if operation is attempted on the current subproject. + */ + void addProject(final Project p) { + if(p == project) { + throw new CannotUseCurrentProjectException('The current project cannot be added in this way. Use configurations instead',p) + } else { + projectConfigurations[p] = [] + } + } + + /** Adds all of the configurations for a project. + * + *

If the project has been added previously, the existing list of configurations will be + * overriden by this one. + * + * @param s Project name for which all configurations needs to be added. If it is the current project + * and exception will be thrown. + * @throw {@link CannotUseCurrentProjectException} if operation is attempted on the current subproject. + */ + void addProject(final String s) { + addProject(project.findProject(s)) + } + + /* Adds one or more configurations from a given project. + * + *

If the project has been added previously, the existing list of configurations will be + * overriden by this one. + * + * @param p Project for configurations needs to be added + * @param config1 First configuration from project + * @param configs Remainder of configurations from project + * @throw {@link CannotUseCurrentProjectException} if operation is attempted on the current subproject. + */ + void addProject(final Project p, Object config1, Object... configs) { + if(p == project) { + throw new CannotUseCurrentProjectException('The current project cannot be added in this way. Use configurations instead',p) + } else { + final List cfg = [config1] + cfg.addAll(configs) + projectConfigurations[p] = cfg + } + } + + /* Adds one or more configurations from a given project. + * + *

If the project has been added previously, the existing list of configurations will be + * overriden by this one. + * + * @param p Project for configurations needs to be added + * @param config1 First configuration from project + * @param configs Remainder of configurations from project + * @throw {@link CannotUseCurrentProjectException} if operation is attempted on the current subproject. + */ + void addProject(final String s, Object config1, Object... configs) { + addProject(project.findProject(s),config1,configs) + } + /** Adds remote repositories as per Gradle convention. * * @param repoConfigurator A closure that is suitable to delegate to a {@code RepositoryHandler} @@ -148,25 +219,22 @@ class OfflineRepositorySync extends DefaultTask { this.repositories } - /** Obtains a list of all the dependencies contained within + /** Obtains a list of all the external module dependencies contained within * the provided configurations * * @return A set of all the dependencies */ - @CompileDynamic Set getDependencies() { - Set deps = [] + final Set deps = [] getConfigurations().each { Configuration cfg -> - cfg.allDependencies.all { - deps.add(it) - } + deps.addAll(getExternalModuleDependencies(cfg)) } if(includeBuildScriptDependencies) { - project.buildscript.configurations.classpath.allDependencies.all { - deps.add(it) - } + deps.addAll(getExternalModuleDependencies( + project.rootProject.buildscript.configurations.getByName('classpath') + )) } deps @@ -263,7 +331,6 @@ class OfflineRepositorySync extends DefaultTask { } @PackageScope -// @CompileDynamic void setAntLogLevel() { if(ivyAnt) { org.apache.tools.ant.Project localRef = ivyAnt.project @@ -291,11 +358,6 @@ class OfflineRepositorySync extends DefaultTask { } } - private Object repoRoot - private List configurations = [] - private RepositoryHandler repositories - private AntBuilder ivyAnt - /** Returns the JAR path to be used for loading IVY. * * @param project @@ -335,5 +397,46 @@ class OfflineRepositorySync extends DefaultTask { new DefaultRepositoryHandler(new BaseRepositoryFactory(), instantiator) } - static private boolean DONT_LOOK_FOR_IVY_JAR = System.getProperty('DONT_LOOK_FOR_IVY_JAR') + private void addConfigurationsRecursivelyFrom(final Project p) { + + if( p != project ) { + projectConfigurations[p] = [] + } + + p.childProjects.each { final String name, final Project child -> + addConfigurationsRecursivelyFrom(child) + } + } + + // Given a project and associated list of configs returns the config objects + // if configs is null or empty all configurations will be returned. + private Iterable getConfigurationsFor(final Project p, Iterable configs) { + if(!configs || configs.size()<1) { + p.configurations + } else { + configs.collect { Object config -> + if(config instanceof Configuration) { + (Configuration)config + } else { + p.configurations.getByName(StringUtils.stringize(config)) + } + } + } + } + + private Iterable getExternalModuleDependencies(final Configuration configuration) { + configuration.allDependencies.findAll { Dependency dep -> + dep instanceof ExternalModuleDependency + } + } + + private Object repoRoot + private final RepositoryHandler repositories + private final AntBuilder ivyAnt + private final List configurations = [] + private final Map > projectConfigurations = [:] + + private static final String LOCALREPONAME = '~~~local~~~repo~~~' + private static final String REMOTECHAINNAME = '~~~remote~~~resolvers~~~' + private static boolean DONT_LOOK_FOR_IVY_JAR = System.getProperty('DONT_LOOK_FOR_IVY_JAR') } diff --git a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/RepositoryTraits.groovy b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/RepositoryTraits.groovy index 192d33b..337269f 100644 --- a/src/main/groovy/org/ysb33r/gradle/ivypot/internal/RepositoryTraits.groovy +++ b/src/main/groovy/org/ysb33r/gradle/ivypot/internal/RepositoryTraits.groovy @@ -20,7 +20,7 @@ import org.gradle.api.Action import org.gradle.api.GradleException import org.gradle.api.artifacts.repositories.AuthenticationContainer import org.gradle.api.artifacts.repositories.PasswordCredentials -import org.ysb33r.gradle.olifant.UriUtils +import org.ysb33r.grolifant.api.UriUtils /** * diff --git a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy index 39c3694..9e2b1dd 100644 --- a/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy +++ b/src/test/groovy/org/ysb33r/gradle/ivypot/OfflineRepositorySyncSpec.groovy @@ -15,6 +15,7 @@ package org.ysb33r.gradle.ivypot import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.repositories.IvyArtifactRepository import org.gradle.testfixtures.ProjectBuilder @@ -37,7 +38,7 @@ class OfflineRepositorySyncSpec extends Specification { syncTask = project.tasks.create('syncTask',OfflineRepositorySync) } - def "Setting up repositories" () { + void "Setting up repositories" () { given: project.allprojects { @@ -178,7 +179,7 @@ class OfflineRepositorySyncSpec extends Specification { //and: 'a flatDir repo can be added' } - def "Not specifying a configuration, means all configurations are loaded"() { + void "Not specifying a configuration, means all configurations are loaded"() { given: project.allprojects { syncTask { @@ -190,15 +191,15 @@ class OfflineRepositorySyncSpec extends Specification { config2 } } - ConfigurationContainer configs = syncTask.configurations + Iterable configs = syncTask.configurations expect: - configs.getByName('config1') != null - configs.getByName('config2') != null + getConfiguration(configs,'config1') != null + getConfiguration(configs,'config2') != null configs.size() == 2 } - def "Specifying configurations means only those are added"() { + void "Specifying configurations means only those are added"() { given: project.allprojects { syncTask { @@ -210,17 +211,57 @@ class OfflineRepositorySyncSpec extends Specification { config2 } } - ConfigurationContainer configs = syncTask.configurations when: - configs.getByName('config2') != null + Iterable configs = syncTask.configurations then: - thrown(org.gradle.api.UnknownDomainObjectException) + getConfiguration(configs,'config2') == null and: - configs.getByName('config1') != null + getConfiguration(configs,'config1') != null configs.size() == 1 } + void "Configurations can be added by instance"() { + project.allprojects { + configurations { + config1 + config2 + } + syncTask { + configurations project.configurations.getByName('config1') + } + + } + + when: + Iterable configs = syncTask.configurations + + then: + getConfiguration(configs,'config2') == null + + and: + getConfiguration(configs,'config1') != null + configs.size() == 1 + + } + + void "Cannot use existing project in alls to addProject"() { + when: + project.allprojects { + syncTask { + addProject project + } + } + + then: + thrown(CannotUseCurrentProjectException) + } + + private getConfiguration(final Iterable configs,final String name) { + configs.find { + it.name == name + } + } } \ No newline at end of file