diff --git a/cli/src/main/scala-2.11/coursier/cli/Helper.scala b/cli/src/main/scala-2.11/coursier/cli/Helper.scala index fdd230cbe..571885178 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Helper.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Helper.scala @@ -273,11 +273,13 @@ class Helper( } } + val userEnabledProfiles = profile.toSet val startRes = Resolution( dependencies.toSet, forceVersions = forceVersions, - filter = Some(dep => keepOptional || !dep.optional) + filter = Some(dep => keepOptional || !dep.optional), + profileActivation = Some(core.Resolution.userProfileActivation(userEnabledProfiles)) ) val loggerFallbackMode = diff --git a/cli/src/main/scala-2.11/coursier/cli/Options.scala b/cli/src/main/scala-2.11/coursier/cli/Options.scala index 5439722a2..4fa545714 100644 --- a/cli/src/main/scala-2.11/coursier/cli/Options.scala +++ b/cli/src/main/scala-2.11/coursier/cli/Options.scala @@ -74,6 +74,10 @@ case class CommonOptions( @Help("Print dependencies as an inversed tree (dependees as children)") @Short("T") reverseTree: Boolean, + @Help("Enable profile") + @Value("profile") + @Short("F") + profile: List[String], @Recurse cacheOptions: CacheOptions ) { diff --git a/core/shared/src/main/scala/coursier/core/Resolution.scala b/core/shared/src/main/scala/coursier/core/Resolution.scala index a8a031d50..5241a3335 100644 --- a/core/shared/src/main/scala/coursier/core/Resolution.scala +++ b/core/shared/src/main/scala/coursier/core/Resolution.scala @@ -239,13 +239,23 @@ object Resolution { var dep = dep0 for ((mgmtConfig, mgmtDep) <- dict.get(DepMgmt.key(dep0))) { - if (dep.version.isEmpty) + + if (mgmtDep.version.nonEmpty) dep = dep.copy(version = mgmtDep.version) - if (config.isEmpty) + + if (mgmtConfig.nonEmpty) config = mgmtConfig + // FIXME The version and scope/config from dependency management, if any, are substituted + // no matter what. The same is not done for the exclusions and optionality, for a lack of + // way of distinguishing empty exclusions from no exclusion section and optional set to + // false from no optional section in the dependency management for now. + if (dep.exclusions.isEmpty) dep = dep.copy(exclusions = mgmtDep.exclusions) + + if (mgmtDep.optional) + dep = dep.copy(optional = mgmtDep.optional) } (config, dep) @@ -434,24 +444,26 @@ object Resolution { activation: Activation, props: Map[String, String] ): Boolean = - if (activation.properties.isEmpty) - false - else - activation - .properties - .forall {case (name, valueOpt) => - props - .get(name) - .exists{ v => - valueOpt - .forall { reqValue => - if (reqValue.startsWith("!")) - v != reqValue.drop(1) - else - v == reqValue - } + activation.properties.nonEmpty && + activation.properties.forall { + case (name, valueOpt) => + props.get(name).exists { v => + valueOpt.forall { reqValue => + if (reqValue.startsWith("!")) + v != reqValue.drop(1) + else + v == reqValue } - } + } + } + + def userProfileActivation(userProfiles: Set[String])( + id: String, + activation: Activation, + props: Map[String, String] + ): Boolean = + userProfiles(id) || + defaultProfileActivation(id, activation, props) /** * Default dependency filter used during resolution. @@ -820,8 +832,6 @@ final case class Resolution( */ // A bit fragile, but seems to work - // TODO Add non regression test for the touchy org.glassfish.jersey.core:jersey-client:2.19 - // (for the way it uses org.glassfish.hk2:hk2-bom,2.4.0-b25) val approxProperties0 = project.parent @@ -841,17 +851,27 @@ final case class Resolution( // 1.2 made from Pom.scala (TODO look at the very details?) // 1.3 & 1.4 (if only vaguely so) - val dependencies0 = addDependencies( - (project.dependencies +: profiles0.map(_.dependencies)).map(withProperties(_, approxProperties)) - ) - val dependenciesMgmt0 = addDependencies( - (project.dependencyManagement +: profiles0.map(_.dependencyManagement)).map(withProperties(_, approxProperties)) - ) val properties0 = (project.properties /: profiles0) { (acc, p) => acc ++ p.properties } + val project0 = project.copy( + properties = project.parent // belongs to 1.5 & 1.6 + .filter(projectCache.contains) + .map(projectCache(_)._2.properties) + .fold(properties0)(_ ++ properties0) + ) + + val propertiesMap0 = propertiesMap(projectProperties(project0)) + + val dependencies0 = addDependencies( + (project0.dependencies +: profiles0.map(_.dependencies)).map(withProperties(_, propertiesMap0)) + ) + val dependenciesMgmt0 = addDependencies( + (project0.dependencyManagement +: profiles0.map(_.dependencyManagement)).map(withProperties(_, propertiesMap0)) + ) + val deps0 = dependencies0 .collect { case ("import", dep) => @@ -861,7 +881,7 @@ final case class Resolution( .collect { case ("import", dep) => dep.moduleVersion } ++ - project.parent // belongs to 1.5 & 1.6 + project0.parent // belongs to 1.5 & 1.6 val deps = deps0.filter(projectCache.contains) @@ -869,35 +889,31 @@ final case class Resolution( .map(projectCache(_)._2) val depMgmt = ( - project.dependencyManagement +: ( + project0.dependencyManagement +: ( profiles0.map(_.dependencyManagement) ++ projs.map(_.dependencyManagement) ) ) - .map(withProperties(_, approxProperties)) + .map(withProperties(_, propertiesMap0)) .foldLeft(Map.empty[DepMgmt.Key, (String, Dependency)])(DepMgmt.addSeq) val depsSet = deps.toSet - project.copy( - version = substituteProps(project.version, approxProperties), + project0.copy( + version = substituteProps(project0.version, propertiesMap0), dependencies = dependencies0 .filterNot{case (config, dep) => config == "import" && depsSet(dep.moduleVersion) } ++ - project.parent // belongs to 1.5 & 1.6 + project0.parent // belongs to 1.5 & 1.6 .filter(projectCache.contains) .toSeq .flatMap(projectCache(_)._2.dependencies), dependencyManagement = depMgmt.values.toSeq .filterNot{case (config, dep) => config == "import" && depsSet(dep.moduleVersion) - }, - properties = project.parent // belongs to 1.5 & 1.6 - .filter(projectCache.contains) - .map(projectCache(_)._2.properties) - .fold(properties0)(properties0 ++ _) + } ) } diff --git a/tests/shared/src/test/resources/resolutions/org.glassfish.jersey.core/jersey-client/2.19 b/tests/shared/src/test/resources/resolutions/org.glassfish.jersey.core/jersey-client/2.19 new file mode 100644 index 000000000..1e76f8ad5 --- /dev/null +++ b/tests/shared/src/test/resources/resolutions/org.glassfish.jersey.core/jersey-client/2.19 @@ -0,0 +1,13 @@ +javax.annotation:javax.annotation-api:1.2:compile +javax.inject:javax.inject:1:compile +javax.ws.rs:javax.ws.rs-api:2.0.1:compile +org.glassfish.hk2:hk2-api:2.4.0-b25:compile +org.glassfish.hk2:hk2-locator:2.4.0-b25:compile +org.glassfish.hk2:hk2-utils:2.4.0-b25:compile +org.glassfish.hk2:osgi-resource-locator:1.0.1:compile +org.glassfish.hk2.external:aopalliance-repackaged:2.4.0-b25:compile +org.glassfish.hk2.external:javax.inject:2.4.0-b25:compile +org.glassfish.jersey.bundles.repackaged:jersey-guava:2.19:compile +org.glassfish.jersey.core:jersey-client:2.19:compile +org.glassfish.jersey.core:jersey-common:2.19:compile +org.javassist:javassist:3.18.1-GA:compile diff --git a/tests/shared/src/test/scala/coursier/test/CentralTests.scala b/tests/shared/src/test/scala/coursier/test/CentralTests.scala index 2edc9a7bd..bc1fef336 100644 --- a/tests/shared/src/test/scala/coursier/test/CentralTests.scala +++ b/tests/shared/src/test/scala/coursier/test/CentralTests.scala @@ -18,11 +18,18 @@ object CentralTests extends TestSuite { def resolve( deps: Set[Dependency], filter: Option[Dependency => Boolean] = None, - extraRepo: Option[Repository] = None + extraRepo: Option[Repository] = None, + profiles: Set[String] = Set.empty ) = { val repositories0 = extraRepo.toSeq ++ repositories - Resolution(deps, filter = filter) + Resolution( + deps, + filter = filter, + profileActivation = Some( + core.Resolution.userProfileActivation(profiles) + ) + ) .process .run(repositories0) .runF @@ -32,7 +39,8 @@ object CentralTests extends TestSuite { module: Module, version: String, extraRepo: Option[Repository] = None, - configuration: String = "" + configuration: String = "", + profiles: Set[String] = Set.empty ) = async { val attrPathPart = @@ -62,7 +70,7 @@ object CentralTests extends TestSuite { ).split('\n').toSeq val dep = Dependency(module, version, configuration = configuration) - val res = await(resolve(Set(dep), extraRepo = extraRepo)) + val res = await(resolve(Set(dep), extraRepo = extraRepo, profiles = profiles)) val result = res .minDependencies @@ -120,7 +128,7 @@ object CentralTests extends TestSuite { 'logback - { async { val dep = Dependency(Module("ch.qos.logback", "logback-classic"), "1.1.3") - val res = await(resolve(Set(dep))).clearCaches + val res = await(resolve(Set(dep))).clearCaches.clearProfileActivation val expected = Resolution( rootDependencies = Set(dep), @@ -136,7 +144,7 @@ object CentralTests extends TestSuite { 'asm - { async { val dep = Dependency(Module("org.ow2.asm", "asm-commons"), "5.0.2") - val res = await(resolve(Set(dep))).clearCaches + val res = await(resolve(Set(dep))).clearCaches.clearProfileActivation val expected = Resolution( rootDependencies = Set(dep), @@ -153,7 +161,7 @@ object CentralTests extends TestSuite { async { val dep = Dependency(Module("joda-time", "joda-time"), "[2.2,2.8]") val res0 = await(resolve(Set(dep))) - val res = res0.clearCaches + val res = res0.clearCaches.clearProfileActivation val expected = Resolution( rootDependencies = Set(dep), @@ -171,7 +179,8 @@ object CentralTests extends TestSuite { 'spark - { resolutionCheck( Module("org.apache.spark", "spark-core_2.11"), - "1.3.1" + "1.3.1", + profiles = Set("hadoop-2.2") ) } @@ -207,6 +216,13 @@ object CentralTests extends TestSuite { ) } + 'projectProperties - { + resolutionCheck( + Module("org.glassfish.jersey.core", "jersey-client"), + "2.19" + ) + } + 'parentDependencyManagementProperties - { resolutionCheck( Module("com.nativelibs4java", "jnaerator-runtime"), diff --git a/tests/shared/src/test/scala/coursier/test/package.scala b/tests/shared/src/test/scala/coursier/test/package.scala index e3dc40cfa..7a2266b6d 100644 --- a/tests/shared/src/test/scala/coursier/test/package.scala +++ b/tests/shared/src/test/scala/coursier/test/package.scala @@ -23,6 +23,8 @@ package object test { ) def clearFilter: Resolution = underlying.copy(filter = None) + def clearProfileActivation: Resolution = + underlying.copy(profileActivation = None) } object Profile {