From 565acc4e2aae5147a1d86ce754b4e82e66ced44d Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 28 Nov 2013 13:17:17 +0000 Subject: [PATCH 01/13] Get rid of sources/docs configurations --- .../sbt/librarymanagement/Artifact.scala | 49 ++++++++++++------- .../sbt/librarymanagement/Configuration.scala | 4 +- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala index 2d174f035..d45dd395a 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala @@ -8,10 +8,10 @@ import java.net.URL import sbt.serialization._ final case class Artifact(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL], extraAttributes: Map[String, String]) { - def extra(attributes: (String, String)*) = Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes ++ ModuleID.checkE(attributes)) + def extra(attributes: (String, String)*) = copy(extraAttributes = extraAttributes ++ ModuleID.checkE(attributes)) } -import Configurations.{ config, Docs, Optional, Pom, Sources, Test } +import Configurations.{ config, Optional, Pom, Test } object Artifact { def apply(name: String): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None) @@ -30,12 +30,23 @@ object Artifact { def javadoc(name: String) = classified(name, DocClassifier) def pom(name: String) = Artifact(name, PomType, PomType, None, Pom :: Nil, None) - val DocClassifier = "javadoc" - val SourceClassifier = "sources" - val DocType = "doc" - val SourceType = "src" - val PomType = "pom" - val TestsClassifier = "tests" + // Possible ivy artifact types such that sbt will treat those artifacts at sources / docs + val DefaultSourceTypes = Set("src", "source", "sources") + val DefaultDocTypes = Set("doc", "docs", "javadoc", "javadocs") + + val DocClassifier = "javadoc" + val SourceClassifier = "sources" + + val TestsClassifier = "tests" + // Artifact types used when: + // * artifacts are explicitly created for Maven dependency resolution (see updateClassifiers) + // * declaring artifacts as part of publishing Ivy files. + val DocType = "doc" + val SourceType = "src" + val PomType = "pom" + + assert (DefaultDocTypes contains DocType) + assert (DefaultSourceTypes contains SourceType) def extract(url: URL, default: String): String = extract(url.toString, default) def extract(name: String, default: String): String = @@ -62,18 +73,18 @@ object Artifact { base + "-" + module.revision + classifierStr + "." + artifact.extension } - val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs) - val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) - def classifierConf(classifier: String): Configuration = - if (classifier.startsWith(TestsClassifier)) - Test - else - classifierConfMap.getOrElse(classifier, Optional) - def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) - def classified(name: String, classifier: String): Artifact = - Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) + val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) + // TODO this function shouldn't exist. Configuration should not just be conjured up like that. + def classifierConf(classifier: String): Configuration = + if(classifier.startsWith(TestsClassifier)) + Test + else + Optional + def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) + def classified(name: String, classifier: String): Artifact = + Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) - private val optStringPickler = implicitly[Pickler[Option[String]]] + private val optStringPickler = implicitly[Pickler[Option[String]]] private val optStringUnpickler = implicitly[Unpickler[Option[String]]] private val vectorConfigurationPickler = implicitly[Pickler[Vector[Configuration]]] private val vectorConfigurationUnpickler = implicitly[Unpickler[Vector[Configuration]]] diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/Configuration.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/Configuration.scala index 50c432682..f860d4abc 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/Configuration.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/Configuration.scala @@ -10,7 +10,7 @@ object Configurations { def default: Seq[Configuration] = defaultMavenConfigurations def defaultMavenConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test, Provided, Optional) def defaultInternal: Seq[Configuration] = Seq(CompileInternal, RuntimeInternal, TestInternal) - def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom) + def auxiliary: Seq[Configuration] = Seq(Pom) def names(cs: Seq[Configuration]) = cs.map(_.name) lazy val RuntimeInternal = optionalInternal(Runtime) @@ -34,10 +34,8 @@ object Configurations { lazy val Compile = config("compile") lazy val IntegrationTest = config("it") extend (Runtime) lazy val Provided = config("provided") - lazy val Docs = config("docs") lazy val Runtime = config("runtime") extend (Compile) lazy val Test = config("test") extend (Runtime) - lazy val Sources = config("sources") lazy val System = config("system") lazy val Optional = config("optional") lazy val Pom = config("pom") From 83fbb212d9c1055f5bdd39002aca58c58b738f27 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 29 Nov 2013 09:33:21 +0000 Subject: [PATCH 02/13] Add include rules to ModuleID --- .../main/scala/sbt/internal/librarymanagement/Ivy.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala index c39c30565..467e9d137 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala @@ -140,7 +140,8 @@ final class IvySbt(val configuration: IvyConfiguration) { /** * Cleans cached resolution cache. - * @param md - module descriptor of the original Ivy graph. + * + * @param md - module descriptor of the original Ivy graph. */ private[sbt] def cleanCachedResolutionCache(md: ModuleDescriptor, log: Logger): Unit = withIvy(log) { i => @@ -620,6 +621,12 @@ private[sbt] object IvySbt { dependencyDescriptor.addExcludeRule(conf, IvyScala.excludeRule(excls.organization, excls.name, excls.configurations, excls.artifact)) } } + for (incls <- dependency.inclusions) { + for (conf <- dependencyDescriptor.getModuleConfigurations) { + dependencyDescriptor.addIncludeRule(conf, IvyScala.includeRule(incls.organization, incls.name, incls.configurations, incls.artifact)) + } + } + dependencyDescriptor } def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit): Unit = From c7dfbbf7051e919413b275fa005235df442d0451 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 29 Nov 2013 09:37:53 +0000 Subject: [PATCH 03/13] Add ArtifactTypeFilter --- .../sbt/librarymanagement/IvyInterface.scala | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala index b582a51ed..62842755e 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala @@ -8,6 +8,8 @@ import java.net.{ URI, URL } import scala.xml.NodeSeq import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver } import org.apache.ivy.util.url.CredentialsStore +import org.apache.ivy.core.module.descriptor +import org.apache.ivy.util.filter.{Filter => IvyFilter} import sbt.serialization._ /** Additional information about a project module */ @@ -31,6 +33,25 @@ object ExclusionRule { implicit val pickler: Pickler[ExclusionRule] with Unpickler[ExclusionRule] = PicklerUnpickler.generate[ExclusionRule] } +/** Work around the inadequacy of Ivy's ArtifactTypeFilter (that it cannot reverse a filter) + * @param types represents the artifact types that we should try to resolve for (as in the allowed values of + * `artifact[type]` from a dependency `` section). One can use this to filter + * source / doc artifacts. + * @param inverted whether to invert the types filter (i.e. allow only types NOT in the set) */ +case class ArtifactTypeFilter(types: Set[String], inverted: Boolean) { + def invert = copy(inverted = !inverted) + def apply(a: descriptor.Artifact): Boolean = (types contains a.getType) ^ inverted +} + +object ArtifactTypeFilter { + def allow(types: Set[String]) = ArtifactTypeFilter(types, false) + def forbid(types: Set[String]) = ArtifactTypeFilter(types, true) + + implicit def toIvyFilter(f: ArtifactTypeFilter): IvyFilter = new IvyFilter { + override def accept(o: Object): Boolean = Option(o) exists { case a: descriptor.Artifact => f.apply(a) } + } +} + final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver) object ModuleConfiguration { def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver) From 00bcd222e678a77c8169fd01a7ca8adf8e83ff29 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 29 Nov 2013 09:38:22 +0000 Subject: [PATCH 04/13] ExclusionRule -> InclExclRule --- .../sbt/librarymanagement/IvyInterface.scala | 15 +++++++++++---- .../scala/sbt/librarymanagement/ModuleID.scala | 4 ++-- .../scala/sbt/librarymanagement/package.scala | 9 +++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 librarymanagement/src/main/scala/sbt/librarymanagement/package.scala diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala index 62842755e..ed99f6bd1 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala @@ -27,10 +27,17 @@ final case class ScmInfo(browseUrl: URL, connection: String, devConnection: Opti final case class Developer(id: String, name: String, email: String, url: URL) -/** Rule to exclude unwanted dependencies pulled in transitively by a module. */ -final case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil) -object ExclusionRule { - implicit val pickler: Pickler[ExclusionRule] with Unpickler[ExclusionRule] = PicklerUnpickler.generate[ExclusionRule] +/** Rule to either: + *
    + *
  • exclude unwanted dependencies pulled in transitively by a module, or to
  • + *
  • include and merge artifacts coming from the ModuleDescriptor if "dependencyArtifacts" are also provided.
  • + *
+ * Which one depends on the parameter name which it is passed to, but the filter has the same fields in both cases. */ +final case class InclExclRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil) +object InclExclRule { + def everything = InclExclRule("*", "*", "*", Nil) + + implicit val pickler: Pickler[InclExclRule] with Unpickler[InclExclRule] = PicklerUnpickler.generate[InclExclRule] } /** Work around the inadequacy of Ivy's ArtifactTypeFilter (that it cannot reverse a filter) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala index 46b63ce39..d2071780c 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala @@ -70,10 +70,10 @@ final case class ModuleID(organization: String, name: String, revision: String, * Applies the provided exclusions to dependencies of this module. Note that only exclusions that specify * both the exact organization and name and nothing else will be included in a pom.xml. */ - def excludeAll(rules: ExclusionRule*) = copy(exclusions = this.exclusions ++ rules) + def excludeAll(rules: InclExclRule*) = copy(exclusions = this.exclusions ++ rules) /** Excludes the dependency with organization `org` and `name` from being introduced by this dependency during resolution. */ - def exclude(org: String, name: String) = excludeAll(ExclusionRule(org, name)) + def exclude(org: String, name: String) = excludeAll(InclExclRule(org, name)) /** * Adds extra attributes for this module. All keys are prefixed with `e:` if they are not already so prefixed. diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/package.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/package.scala new file mode 100644 index 000000000..24e6eee19 --- /dev/null +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/package.scala @@ -0,0 +1,9 @@ +package sbt + +package object librarymanagement { + type ExclusionRule = InclExclRule + val ExclusionRule = InclExclRule + + type InclusionRule = InclExclRule + val InclusionRule = InclExclRule +} From 789ebee0f1c2d63d3f6e8b17913d36acf1a93a5a Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 29 Nov 2013 09:48:46 +0000 Subject: [PATCH 05/13] ModuleID + inclusions --- .../scala/sbt/librarymanagement/IvyScala.scala | 15 +++++++++++++-- .../scala/sbt/librarymanagement/ModuleID.scala | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala index e5808eef9..a9b5f54e4 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala @@ -6,8 +6,7 @@ package sbt.librarymanagement import java.util.Collections.emptyMap import scala.collection.mutable.HashSet -import org.apache.ivy.core.module.descriptor.{ DefaultExcludeRule, ExcludeRule } -import org.apache.ivy.core.module.descriptor.{ DependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor, OverrideDependencyDescriptorMediator } +import org.apache.ivy.core.module.descriptor._ import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } import org.apache.ivy.plugins.matcher.ExactPatternMatcher import sbt.util.Logger @@ -125,4 +124,16 @@ private[sbt] object IvyScala { configurationNames.foreach(rule.addConfiguration) rule } + + /** + * Creates an IncludeRule that includes artifacts with the given module organization and name for + * the given configurations. + */ + private[sbt] def includeRule(organization: String, name: String, configurationNames: Iterable[String], includeTypePattern: String): IncludeRule = + { + val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", includeTypePattern, "*") + val rule = new DefaultIncludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef]) + configurationNames.foreach(rule.addConfiguration) + rule + } } diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala index d2071780c..3e2948394 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/ModuleID.scala @@ -8,7 +8,7 @@ import java.net.URL import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties import sbt.serialization._ -final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, isForce: Boolean = false, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String, String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled) { +final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, isForce: Boolean = false, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, inclusions: Seq[InclusionRule] = Nil, extraAttributes: Map[String, String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled) { override def toString: String = organization + ":" + name + ":" + revision + (configurations match { case Some(s) => ":" + s; case None => "" }) + From 9db3e86fd94f9893faf31add53cfe8364a9a09db Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 29 Nov 2013 10:11:27 +0000 Subject: [PATCH 06/13] [1/2] Add {source,doc}ArtifactTypes settings, use them in update* tasks in conjunction with ArtifactTypeFilter --- .../librarymanagement/IvyActions.scala | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala index 8d646ee65..3bb0cbfc7 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala @@ -29,21 +29,27 @@ final class PublishConfiguration(val ivyFile: Option[File], val resolverName: St this(ivyFile, resolverName, artifacts, checksums, logging, false) } -final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val missingOk: Boolean, val logging: UpdateLogging.Value) { +final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val missingOk: Boolean, val logging: UpdateLogging.Value, val artifactFilter: ArtifactTypeFilter) { + @deprecated("You should use the constructor that provides an artifactFilter", "1.0.x") + def this(retrieve: Option[RetrieveConfiguration], missingOk: Boolean, logging: UpdateLogging.Value) { + this(retrieve, missingOk, logging, ArtifactTypeFilter.forbid(Set("src", "doc"))) // allow everything but "src", "doc" by default + } + private[sbt] def copy( retrieve: Option[RetrieveConfiguration] = this.retrieve, missingOk: Boolean = this.missingOk, - logging: UpdateLogging.Value = this.logging + logging: UpdateLogging.Value = this.logging, + artifactFilter: ArtifactTypeFilter = this.artifactFilter ): UpdateConfiguration = - new UpdateConfiguration(retrieve, missingOk, logging) + new UpdateConfiguration(retrieve, missingOk, logging, artifactFilter) } final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String, val sync: Boolean, val configurationsToRetrieve: Option[Set[Configuration]]) { def this(retrieveDirectory: File, outputPattern: String) = this(retrieveDirectory, outputPattern, false, None) def this(retrieveDirectory: File, outputPattern: String, sync: Boolean) = this(retrieveDirectory, outputPattern, sync, None) } final case class MakePomConfiguration(file: File, moduleInfo: ModuleInfo, configurations: Option[Seq[Configuration]] = None, extra: NodeSeq = NodeSeq.Empty, process: XNode => XNode = n => n, filterRepositories: MavenRepository => Boolean = _ => true, allRepositories: Boolean, includeTypes: Set[String] = Set(Artifact.DefaultType, Artifact.PomType)) -// exclude is a map on a restricted ModuleID -final case class GetClassifiersConfiguration(module: GetClassifiersModule, exclude: Map[ModuleID, Set[String]], configuration: UpdateConfiguration, ivyScala: Option[IvyScala]) +/** @param exclude is a map from ModuleID to classifiers that were previously tried and failed, so should now be excluded */ +final case class GetClassifiersConfiguration(module: GetClassifiersModule, exclude: Map[ModuleID, Set[String]], configuration: UpdateConfiguration, ivyScala: Option[IvyScala], sourceArtifactTypes: Set[String], docArtifactTypes: Set[String]) final case class GetClassifiersModule(id: ModuleID, modules: Seq[ModuleID], configurations: Seq[Configuration], classifiers: Seq[String]) final class UnresolvedWarningConfiguration private[sbt] ( @@ -180,6 +186,7 @@ object IvyActions { val resolveOptions = new ResolveOptions val resolveId = ResolveOptions.getDefaultResolveId(md) resolveOptions.setResolveId(resolveId) + resolveOptions.setArtifactFilter(configuration.artifactFilter) resolveOptions.setLog(ivyLogLevel(configuration.logging)) x.customResolve(md, configuration.missingOk, logicalClock, resolveOptions, depDir getOrElse { sys.error("dependency base directory is not specified") }, log) match { case Left(x) => @@ -194,7 +201,7 @@ object IvyActions { case (ivy, md, default) => val iw = IvySbt.inconsistentDuplicateWarning(md) iw foreach { log.warn(_) } - val (report, err) = resolve(configuration.logging)(ivy, md, default) + val (report, err) = resolve(configuration.logging)(ivy, md, default, configuration.artifactFilter) err match { case Some(x) if !configuration.missingOk => Left(UnresolvedWarning(x, uwconfig)) @@ -298,11 +305,13 @@ object IvyActions { private[this] def restrictedCopy(m: ModuleID, confs: Boolean) = ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, extraAttributes = m.extraAttributes, configurations = if (confs) m.configurations else None) - private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) = + + private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String, filter: ArtifactTypeFilter): (ResolveReport, Option[ResolveException]) = { val resolveOptions = new ResolveOptions val resolveId = ResolveOptions.getDefaultResolveId(module) resolveOptions.setResolveId(resolveId) + resolveOptions.setArtifactFilter(filter) resolveOptions.setLog(ivyLogLevel(logging)) ResolutionCache.cleanModule(module.getModuleRevisionId, resolveId, ivy.getSettings.getResolutionCacheManager) val resolveReport = ivy.resolve(module, resolveOptions) From 055859ea9aab298cd39a7fd768d63e095886a314 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 29 Nov 2013 10:17:13 +0000 Subject: [PATCH 07/13] [2/2] updateClassifiers to 1) filter artifacts by type 2) ALSO retrieve artifacts defined in Ivy publications --- .../librarymanagement/IvyActions.scala | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala index 3bb0cbfc7..8d43601fe 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala @@ -250,7 +250,15 @@ object IvyActions { def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, log: Logger): UpdateReport = updateClassifiers(ivySbt, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, Vector(), log) - // artifacts can be obtained from calling toSeq on UpdateReport + /** + * Creates explicit artifacts for each classifier in `config.module`, and then attempts to resolve them directly. This + * is for Maven compatibility, where these artifacts are not "published" in the POM, so they don't end up in the Ivy + * that sbt generates for them either.
+ * Artifacts can be obtained from calling toSeq on UpdateReport.
+ * In addition, retrieves specific Ivy artifacts if they have one of the requested `config.configuration.types`. + * @param config important to set `config.configuration.types` to only allow artifact types that can correspond to + * "classified" artifacts (sources and javadocs). + */ private[sbt] def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], artifacts: Vector[(String, ModuleID, Artifact, File)], @@ -259,14 +267,27 @@ object IvyActions { import config.{ configuration => c, module => mod, _ } import mod.{ configurations => confs, _ } assert(classifiers.nonEmpty, "classifiers cannot be empty") + assert(c.artifactFilter.types.nonEmpty, "UpdateConfiguration must filter on some types") val baseModules = modules map { m => restrictedCopy(m, true) } // Adding list of explicit artifacts here. val deps = baseModules.distinct flatMap classifiedArtifacts(classifiers, exclude, artifacts) val base = restrictedCopy(id, true).copy(name = id.name + classifiers.mkString("$", "_", "")) val module = new ivySbt.Module(InlineConfigurationWithExcludes(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala, configurations = confs)) - val upConf = new UpdateConfiguration(c.retrieve, true, c.logging) + // c.copy ensures c.types is preserved too + val upConf = c.copy(missingOk = true) updateEither(module, upConf, uwconfig, logicalClock, depDir, log) match { - case Right(r) => r + case Right(r) => + // The artifacts that came from Ivy don't have their classifier set, let's set it according to + // FIXME: this is only done because IDE plugins depend on `classifier` to determine type. They + val typeClassifierMap: Map[String, String] = + ((sourceArtifactTypes.toIterable map (_ -> Artifact.SourceClassifier)) + :: (docArtifactTypes.toIterable map (_ -> Artifact.DocClassifier)) :: Nil).flatten.toMap + r.substitute { (conf, mid, artFileSeq) => + artFileSeq map { case (art, f) => + // Deduce the classifier from the type if no classifier is present already + art.copy(classifier = art.classifier orElse typeClassifierMap.get(art.`type`)) -> f + } + } case Left(w) => throw w.resolveException } @@ -282,7 +303,7 @@ object IvyActions { { val arts = (artifacts collect { case (_, x, art, _) if sameModule(m, x) && art.classifier.isDefined => art }).distinct if (arts.isEmpty) None - else Some(m.copy(isTransitive = false, explicitArtifacts = arts)) + else Some(intransitiveModuleWithExplicitArts(m, arts)) } def hardcodedArtifacts = classifiedArtifacts(classifiers, exclude)(m) explicitArtifacts orElse hardcodedArtifacts @@ -291,8 +312,27 @@ object IvyActions { { val excluded = exclude getOrElse (restrictedCopy(m, false), Set.empty) val included = classifiers filterNot excluded - if (included.isEmpty) None else Some(m.copy(isTransitive = false, explicitArtifacts = classifiedArtifacts(m.name, included))) + if (included.isEmpty) None else { + Some(intransitiveModuleWithExplicitArts(module = m, arts = classifiedArtifacts(m.name, included))) + } } + + /** + * Explicitly set an "include all" rule (the default) because otherwise, if we declare ANY explicitArtifacts, + * [[org.apache.ivy.core.resolve.IvyNode#getArtifacts]] (in Ivy 2.3.0-rc1) will not merge in the descriptor's + * artifacts and will only keep the explicitArtifacts. + *
+ * Look for the comment saying {{{ + * // and now we filter according to include rules + * }}} + * in `IvyNode`, which iterates on `includes`, which will ordinarily be empty because higher up, in {{{ + * addAllIfNotNull(includes, usage.getDependencyIncludesSet(rootModuleConf)); + * }}} + * `usage.getDependencyIncludesSet` returns null if there are no (explicit) include rules. + */ + private def intransitiveModuleWithExplicitArts(module: ModuleID, arts: Seq[Artifact]): ModuleID = + module.copy(isTransitive = false, explicitArtifacts = arts, inclusions = InclExclRule.everything :: Nil) + def addExcluded(report: UpdateReport, classifiers: Seq[String], exclude: Map[ModuleID, Set[String]]): UpdateReport = report.addMissing { id => classifiedArtifacts(id.name, classifiers filter getExcluded(id, exclude)) } def classifiedArtifacts(name: String, classifiers: Seq[String]): Seq[Artifact] = From 55d13bd8cd30f1d9b772b7a896b7a1193e29e828 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Wed, 4 Dec 2013 13:29:35 +0000 Subject: [PATCH 08/13] Fix artifact, deprecate stuff --- .../main/scala/sbt/librarymanagement/Artifact.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala index d45dd395a..b5b209bcb 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala @@ -40,7 +40,7 @@ object Artifact { val TestsClassifier = "tests" // Artifact types used when: // * artifacts are explicitly created for Maven dependency resolution (see updateClassifiers) - // * declaring artifacts as part of publishing Ivy files. + // * declaring artifacts as part of creating Ivy files. val DocType = "doc" val SourceType = "src" val PomType = "pom" @@ -74,15 +74,19 @@ object Artifact { } val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) - // TODO this function shouldn't exist. Configuration should not just be conjured up like that. + @deprecated("Configuration should not be decided from the classifier.", "1.0") def classifierConf(classifier: String): Configuration = if(classifier.startsWith(TestsClassifier)) Test else Optional def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) + + /** Create a classified explicit artifact, to be used when trying to resolve sources|javadocs from Maven. This is + * necessary because those artifacts are not published in the Ivy generated from the Pom of the module in question. + * The artifact is created under the default configuration. */ def classified(name: String, classifier: String): Artifact = - Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) + Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), Nil, None) private val optStringPickler = implicitly[Pickler[Option[String]]] private val optStringUnpickler = implicitly[Unpickler[Option[String]]] From dc81a1813d0a4362b6a9b9786b80c928a1637930 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 5 Dec 2013 18:11:12 +0000 Subject: [PATCH 09/13] Fix UpdateReport.substitute to not drop missing artifacts --- .../sbt/internal/librarymanagement/RichUpdateReport.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/RichUpdateReport.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/RichUpdateReport.scala index 61675fd2d..e4005ef21 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/RichUpdateReport.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/RichUpdateReport.scala @@ -45,8 +45,8 @@ final class RichUpdateReport(report: UpdateReport) { moduleReportMap { (configuration, modReport) => val newArtifacts = f(configuration, modReport.module, modReport.artifacts) modReport.copy( - artifacts = f(configuration, modReport.module, modReport.artifacts), - missingArtifacts = Nil + artifacts = newArtifacts, + missingArtifacts = modReport.missingArtifacts ) } From 31440bb1473c2ccb388d629af73456807a50df50 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Mon, 8 Feb 2016 17:12:31 +0000 Subject: [PATCH 10/13] Fix BaseIvySpeficiation's init of UpdateConfiguration with a sensible default ArtifactTypeFilter --- librarymanagement/src/test/scala/BaseIvySpecification.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librarymanagement/src/test/scala/BaseIvySpecification.scala b/librarymanagement/src/test/scala/BaseIvySpecification.scala index b0744fd3b..c6ceb1020 100644 --- a/librarymanagement/src/test/scala/BaseIvySpecification.scala +++ b/librarymanagement/src/test/scala/BaseIvySpecification.scala @@ -61,7 +61,7 @@ trait BaseIvySpecification extends UnitSpec { def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = { // IO.delete(currentTarget) val retrieveConfig = new RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern, false) - val config = new UpdateConfiguration(Some(retrieveConfig), false, UpdateLogging.Full) + val config = new UpdateConfiguration(Some(retrieveConfig), false, UpdateLogging.Full, ArtifactTypeFilter.forbid(Set("src", "doc"))) IvyActions.updateEither(module, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, Some(currentDependency), log) } From 0d4336bbaa8e870a62d86ece49e5136e4b39c330 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Mon, 8 Feb 2016 17:14:04 +0000 Subject: [PATCH 11/13] Apply style fixes --- .../sbt/internal/librarymanagement/Ivy.scala | 4 +- .../librarymanagement/IvyActions.scala | 47 +++++++-------- .../sbt/librarymanagement/Artifact.scala | 58 ++++++++++--------- .../sbt/librarymanagement/IvyInterface.scala | 38 ++++++------ .../sbt/librarymanagement/IvyScala.scala | 12 ++-- 5 files changed, 83 insertions(+), 76 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala index 467e9d137..f6d449c4a 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala @@ -140,8 +140,8 @@ final class IvySbt(val configuration: IvyConfiguration) { /** * Cleans cached resolution cache. - * - * @param md - module descriptor of the original Ivy graph. + * + * @param md - module descriptor of the original Ivy graph. */ private[sbt] def cleanCachedResolutionCache(md: ModuleDescriptor, log: Logger): Unit = withIvy(log) { i => diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala index 8d43601fe..8fc1d4866 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/IvyActions.scala @@ -251,14 +251,14 @@ object IvyActions { updateClassifiers(ivySbt, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, Vector(), log) /** - * Creates explicit artifacts for each classifier in `config.module`, and then attempts to resolve them directly. This - * is for Maven compatibility, where these artifacts are not "published" in the POM, so they don't end up in the Ivy - * that sbt generates for them either.
- * Artifacts can be obtained from calling toSeq on UpdateReport.
- * In addition, retrieves specific Ivy artifacts if they have one of the requested `config.configuration.types`. - * @param config important to set `config.configuration.types` to only allow artifact types that can correspond to - * "classified" artifacts (sources and javadocs). - */ + * Creates explicit artifacts for each classifier in `config.module`, and then attempts to resolve them directly. This + * is for Maven compatibility, where these artifacts are not "published" in the POM, so they don't end up in the Ivy + * that sbt generates for them either.
+ * Artifacts can be obtained from calling toSeq on UpdateReport.
+ * In addition, retrieves specific Ivy artifacts if they have one of the requested `config.configuration.types`. + * @param config important to set `config.configuration.types` to only allow artifact types that can correspond to + * "classified" artifacts (sources and javadocs). + */ private[sbt] def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], artifacts: Vector[(String, ModuleID, Artifact, File)], @@ -283,9 +283,10 @@ object IvyActions { ((sourceArtifactTypes.toIterable map (_ -> Artifact.SourceClassifier)) :: (docArtifactTypes.toIterable map (_ -> Artifact.DocClassifier)) :: Nil).flatten.toMap r.substitute { (conf, mid, artFileSeq) => - artFileSeq map { case (art, f) => - // Deduce the classifier from the type if no classifier is present already - art.copy(classifier = art.classifier orElse typeClassifierMap.get(art.`type`)) -> f + artFileSeq map { + case (art, f) => + // Deduce the classifier from the type if no classifier is present already + art.copy(classifier = art.classifier orElse typeClassifierMap.get(art.`type`)) -> f } } case Left(w) => @@ -318,18 +319,18 @@ object IvyActions { } /** - * Explicitly set an "include all" rule (the default) because otherwise, if we declare ANY explicitArtifacts, - * [[org.apache.ivy.core.resolve.IvyNode#getArtifacts]] (in Ivy 2.3.0-rc1) will not merge in the descriptor's - * artifacts and will only keep the explicitArtifacts. - *
- * Look for the comment saying {{{ - * // and now we filter according to include rules - * }}} - * in `IvyNode`, which iterates on `includes`, which will ordinarily be empty because higher up, in {{{ - * addAllIfNotNull(includes, usage.getDependencyIncludesSet(rootModuleConf)); - * }}} - * `usage.getDependencyIncludesSet` returns null if there are no (explicit) include rules. - */ + * Explicitly set an "include all" rule (the default) because otherwise, if we declare ANY explicitArtifacts, + * [[org.apache.ivy.core.resolve.IvyNode#getArtifacts]] (in Ivy 2.3.0-rc1) will not merge in the descriptor's + * artifacts and will only keep the explicitArtifacts. + *
+ * Look for the comment saying {{{ + * // and now we filter according to include rules + * }}} + * in `IvyNode`, which iterates on `includes`, which will ordinarily be empty because higher up, in {{{ + * addAllIfNotNull(includes, usage.getDependencyIncludesSet(rootModuleConf)); + * }}} + * `usage.getDependencyIncludesSet` returns null if there are no (explicit) include rules. + */ private def intransitiveModuleWithExplicitArts(module: ModuleID, arts: Seq[Artifact]): ModuleID = module.copy(isTransitive = false, explicitArtifacts = arts, inclusions = InclExclRule.everything :: Nil) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala index b5b209bcb..8bf97bc05 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/Artifact.scala @@ -30,23 +30,23 @@ object Artifact { def javadoc(name: String) = classified(name, DocClassifier) def pom(name: String) = Artifact(name, PomType, PomType, None, Pom :: Nil, None) - // Possible ivy artifact types such that sbt will treat those artifacts at sources / docs - val DefaultSourceTypes = Set("src", "source", "sources") - val DefaultDocTypes = Set("doc", "docs", "javadoc", "javadocs") + // Possible ivy artifact types such that sbt will treat those artifacts at sources / docs + val DefaultSourceTypes = Set("src", "source", "sources") + val DefaultDocTypes = Set("doc", "docs", "javadoc", "javadocs") - val DocClassifier = "javadoc" - val SourceClassifier = "sources" + val DocClassifier = "javadoc" + val SourceClassifier = "sources" - val TestsClassifier = "tests" - // Artifact types used when: - // * artifacts are explicitly created for Maven dependency resolution (see updateClassifiers) - // * declaring artifacts as part of creating Ivy files. - val DocType = "doc" - val SourceType = "src" - val PomType = "pom" + val TestsClassifier = "tests" + // Artifact types used when: + // * artifacts are explicitly created for Maven dependency resolution (see updateClassifiers) + // * declaring artifacts as part of creating Ivy files. + val DocType = "doc" + val SourceType = "src" + val PomType = "pom" - assert (DefaultDocTypes contains DocType) - assert (DefaultSourceTypes contains SourceType) + assert(DefaultDocTypes contains DocType) + assert(DefaultSourceTypes contains SourceType) def extract(url: URL, default: String): String = extract(url.toString, default) def extract(name: String, default: String): String = @@ -73,22 +73,24 @@ object Artifact { base + "-" + module.revision + classifierStr + "." + artifact.extension } - val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) - @deprecated("Configuration should not be decided from the classifier.", "1.0") - def classifierConf(classifier: String): Configuration = - if(classifier.startsWith(TestsClassifier)) - Test - else - Optional - def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) + val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) + @deprecated("Configuration should not be decided from the classifier.", "1.0") + def classifierConf(classifier: String): Configuration = + if (classifier.startsWith(TestsClassifier)) + Test + else + Optional + def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) - /** Create a classified explicit artifact, to be used when trying to resolve sources|javadocs from Maven. This is - * necessary because those artifacts are not published in the Ivy generated from the Pom of the module in question. - * The artifact is created under the default configuration. */ - def classified(name: String, classifier: String): Artifact = - Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), Nil, None) + /** + * Create a classified explicit artifact, to be used when trying to resolve sources|javadocs from Maven. This is + * necessary because those artifacts are not published in the Ivy generated from the Pom of the module in question. + * The artifact is created under the default configuration. + */ + def classified(name: String, classifier: String): Artifact = + Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), Nil, None) - private val optStringPickler = implicitly[Pickler[Option[String]]] + private val optStringPickler = implicitly[Pickler[Option[String]]] private val optStringUnpickler = implicitly[Unpickler[Option[String]]] private val vectorConfigurationPickler = implicitly[Pickler[Vector[Configuration]]] private val vectorConfigurationUnpickler = implicitly[Unpickler[Vector[Configuration]]] diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala index ed99f6bd1..964fe7447 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyInterface.scala @@ -9,7 +9,7 @@ import scala.xml.NodeSeq import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver } import org.apache.ivy.util.url.CredentialsStore import org.apache.ivy.core.module.descriptor -import org.apache.ivy.util.filter.{Filter => IvyFilter} +import org.apache.ivy.util.filter.{ Filter => IvyFilter } import sbt.serialization._ /** Additional information about a project module */ @@ -27,12 +27,14 @@ final case class ScmInfo(browseUrl: URL, connection: String, devConnection: Opti final case class Developer(id: String, name: String, email: String, url: URL) -/** Rule to either: - *
    - *
  • exclude unwanted dependencies pulled in transitively by a module, or to
  • - *
  • include and merge artifacts coming from the ModuleDescriptor if "dependencyArtifacts" are also provided.
  • - *
- * Which one depends on the parameter name which it is passed to, but the filter has the same fields in both cases. */ +/** + * Rule to either: + *
    + *
  • exclude unwanted dependencies pulled in transitively by a module, or to
  • + *
  • include and merge artifacts coming from the ModuleDescriptor if "dependencyArtifacts" are also provided.
  • + *
+ * Which one depends on the parameter name which it is passed to, but the filter has the same fields in both cases. + */ final case class InclExclRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil) object InclExclRule { def everything = InclExclRule("*", "*", "*", Nil) @@ -40,23 +42,25 @@ object InclExclRule { implicit val pickler: Pickler[InclExclRule] with Unpickler[InclExclRule] = PicklerUnpickler.generate[InclExclRule] } -/** Work around the inadequacy of Ivy's ArtifactTypeFilter (that it cannot reverse a filter) - * @param types represents the artifact types that we should try to resolve for (as in the allowed values of - * `artifact[type]` from a dependency `` section). One can use this to filter - * source / doc artifacts. - * @param inverted whether to invert the types filter (i.e. allow only types NOT in the set) */ +/** + * Work around the inadequacy of Ivy's ArtifactTypeFilter (that it cannot reverse a filter) + * @param types represents the artifact types that we should try to resolve for (as in the allowed values of + * `artifact[type]` from a dependency `` section). One can use this to filter + * source / doc artifacts. + * @param inverted whether to invert the types filter (i.e. allow only types NOT in the set) + */ case class ArtifactTypeFilter(types: Set[String], inverted: Boolean) { def invert = copy(inverted = !inverted) def apply(a: descriptor.Artifact): Boolean = (types contains a.getType) ^ inverted } object ArtifactTypeFilter { - def allow(types: Set[String]) = ArtifactTypeFilter(types, false) - def forbid(types: Set[String]) = ArtifactTypeFilter(types, true) + def allow(types: Set[String]) = ArtifactTypeFilter(types, false) + def forbid(types: Set[String]) = ArtifactTypeFilter(types, true) - implicit def toIvyFilter(f: ArtifactTypeFilter): IvyFilter = new IvyFilter { - override def accept(o: Object): Boolean = Option(o) exists { case a: descriptor.Artifact => f.apply(a) } - } + implicit def toIvyFilter(f: ArtifactTypeFilter): IvyFilter = new IvyFilter { + override def accept(o: Object): Boolean = Option(o) exists { case a: descriptor.Artifact => f.apply(a) } + } } final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala index a9b5f54e4..d2df9b4cf 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala @@ -130,10 +130,10 @@ private[sbt] object IvyScala { * the given configurations. */ private[sbt] def includeRule(organization: String, name: String, configurationNames: Iterable[String], includeTypePattern: String): IncludeRule = - { - val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", includeTypePattern, "*") - val rule = new DefaultIncludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef]) - configurationNames.foreach(rule.addConfiguration) - rule - } + { + val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", includeTypePattern, "*") + val rule = new DefaultIncludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef]) + configurationNames.foreach(rule.addConfiguration) + rule + } } From fcd52ee2834cdde9eed8b9c276fb83ce2925c46b Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Wed, 10 Feb 2016 13:12:54 +0000 Subject: [PATCH 12/13] Expose the default UpdateConfiguration in BaseIvyConfiguration --- .../src/test/scala/BaseIvySpecification.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/librarymanagement/src/test/scala/BaseIvySpecification.scala b/librarymanagement/src/test/scala/BaseIvySpecification.scala index c6ceb1020..260ac3517 100644 --- a/librarymanagement/src/test/scala/BaseIvySpecification.scala +++ b/librarymanagement/src/test/scala/BaseIvySpecification.scala @@ -58,10 +58,14 @@ trait BaseIvySpecification extends UnitSpec { new InlineIvyConfiguration(paths, resolvers, other, moduleConfs, off, None, check, Some(resCacheDir), uo, log) } + def makeUpdateConfiguration: UpdateConfiguration = { + val retrieveConfig = new RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern, false) + new UpdateConfiguration(Some(retrieveConfig), false, UpdateLogging.Full, ArtifactTypeFilter.forbid(Set("src", "doc"))) + } + def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = { // IO.delete(currentTarget) - val retrieveConfig = new RetrieveConfiguration(currentManaged, Resolver.defaultRetrievePattern, false) - val config = new UpdateConfiguration(Some(retrieveConfig), false, UpdateLogging.Full, ArtifactTypeFilter.forbid(Set("src", "doc"))) + val config = makeUpdateConfiguration IvyActions.updateEither(module, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, Some(currentDependency), log) } From 01030a197a53f3ebb4eca43a01c2d7a23b8ecd5f Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Wed, 10 Feb 2016 13:14:42 +0000 Subject: [PATCH 13/13] Add IvyRepoSpec which tests that source artifacts (in this case, with type=src, though in the same "compile" configuration) are resolved correctly when using IvyActions.updateClassifiers, and NOT resolved when using the normal IvyActions.updateEither. --- .../module-with-srcs/0.1.00/ivys/ivy.xml | 23 +++++ .../0.1.00/jars/libmodule.jar | Bin 0 -> 341 bytes .../0.1.00/srcs/libmodule-source.jar | Bin 0 -> 341 bytes .../librarymanagement/IvyRepoSpec.scala | 89 ++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100755 librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml create mode 100644 librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/jars/libmodule.jar create mode 100644 librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/srcs/libmodule-source.jar create mode 100644 librarymanagement/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala diff --git a/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml b/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml new file mode 100755 index 000000000..ab045d5cb --- /dev/null +++ b/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/ivys/ivy.xml @@ -0,0 +1,23 @@ + + + + + Just a test module that publishes both a binary jar and a src jar in the 'compile' configuration. + + + + + + + + + + + + + + + + + + diff --git a/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/jars/libmodule.jar b/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/jars/libmodule.jar new file mode 100644 index 0000000000000000000000000000000000000000..b21d53c7baf1086d25cdb54e4fe86d41ed542b27 GIT binary patch literal 341 zcmWIWW@Zs#;Nak3xD)E-!GHuf8CV#6T|*poJ^kGD|D9rBU}gyLX6FE@V1gx0>v$BCyF#%yMkUj+BFaQAZm_uOz literal 0 HcmV?d00001 diff --git a/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/srcs/libmodule-source.jar b/librarymanagement/src/test/resources/test-ivy-repo/com.test/module-with-srcs/0.1.00/srcs/libmodule-source.jar new file mode 100644 index 0000000000000000000000000000000000000000..b21d53c7baf1086d25cdb54e4fe86d41ed542b27 GIT binary patch literal 341 zcmWIWW@Zs#;Nak3xD)E-!GHuf8CV#6T|*poJ^kGD|D9rBU}gyLX6FE@V1gx0>v$BCyF#%yMkUj+BFaQAZm_uOz literal 0 HcmV?d00001 diff --git a/librarymanagement/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala b/librarymanagement/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala new file mode 100644 index 000000000..9e15fbc01 --- /dev/null +++ b/librarymanagement/src/test/scala/sbt/internal/librarymanagement/IvyRepoSpec.scala @@ -0,0 +1,89 @@ +package sbt.internal.librarymanagement + +import org.scalatest.Inside +import sbt.internal.librarymanagement.impl.DependencyBuilders +import sbt.librarymanagement._ + +class IvyRepoSpec extends BaseIvySpecification with DependencyBuilders { + + val ourModuleID = ModuleID("com.example", "foo", "0.1.0", Some("compile")) + + def makeModuleForDepWithSources = { + // By default a module seems to only have [compile, test, runtime], yet deps automatically map to + // default->compile(default) ... so I guess we have to explicitly use e.g. "compile" + val dep = "com.test" % "module-with-srcs" % "0.1.00" % "compile" + + module( + ourModuleID, + Seq(dep), None //, UpdateOptions().withCachedResolution(true) + ) + } + + "ivyUpdate from ivy repository" should "resolve only binary artifact from module which also contains a sources artifact under the same configuration." in { + cleanIvyCache() + + val m = makeModuleForDepWithSources + + val report = ivyUpdate(m) + + import Inside._ + inside(report.configuration("compile").map(_.modules)) { + case Some(Seq(mr)) => + inside(mr.artifacts) { + case Seq((ar, _)) => + ar.`type` shouldBe "jar" + ar.extension shouldBe "jar" + } + } + } + + it should "resolve only sources artifact of an acceptable artifact type, \"src\", when calling updateClassifiers." in { + cleanIvyCache() + + val m = makeModuleForDepWithSources + + // the "default" configuration used in updateEither. + val c = makeUpdateConfiguration + + val ivyScala = m.moduleSettings.ivyScala + val srcTypes = Set("src") + val docTypes = Set("javadoc") + // These will be the default classifiers that SBT should try, in case a dependency is Maven. + // In this case though, they will be tried and should fail gracefully - only the + val attemptedClassifiers = Seq("sources", "javadoc") + + // The dep that we want to get the "classifiers" (i.e. sources / docs) for. + // We know it has only one source artifact in the "compile" configuration. + val dep = "com.test" % "module-with-srcs" % "0.1.00" % "compile" + + val clMod = { + import language.implicitConversions + implicit val key = (m: ModuleID) => (m.organization, m.name, m.revision) + val externalModules = Seq(dep) + // Note: need to extract ourModuleID so we can plug it in here, can't fish it back out of the IvySbt#Module (`m`) + GetClassifiersModule(ourModuleID, externalModules, Seq(Configurations.Compile), attemptedClassifiers) + } + + val gcm = GetClassifiersConfiguration(clMod, Map.empty, c.copy(artifactFilter = c.artifactFilter.invert), ivyScala, srcTypes, docTypes) + + val report2 = IvyActions.updateClassifiers(m.owner, gcm, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, Vector(), log) + + import Inside._ + inside(report2.configuration("compile").map(_.modules)) { + case Some(Seq(mr)) => + inside(mr.artifacts) { + case Seq((ar, _)) => + ar.name shouldBe "libmodule-source" + ar.`type` shouldBe "src" + ar.extension shouldBe "jar" + } + } + } + + override lazy val resolvers: Seq[Resolver] = Seq(testIvy) + + lazy val testIvy = { + val repoUrl = getClass.getResource("/test-ivy-repo") + Resolver.url("Test Repo", repoUrl)(Resolver.ivyStylePatterns) + } +}