From 4af9b70bf93db14aeb57fe1f369934270754a07e Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 10 Jan 2013 16:06:13 -0500 Subject: [PATCH] Split IvyInterface into Artifact, Configuration, ModuleID, and Resolver source files --- ivy/src/main/scala/sbt/Artifact.scala | 71 +++ ivy/src/main/scala/sbt/Configuration.scala | 65 +++ ivy/src/main/scala/sbt/IvyInterface.scala | 500 +-------------------- ivy/src/main/scala/sbt/ModuleID.scala | 102 +++++ ivy/src/main/scala/sbt/Resolver.scala | 289 ++++++++++++ 5 files changed, 529 insertions(+), 498 deletions(-) create mode 100644 ivy/src/main/scala/sbt/Artifact.scala create mode 100644 ivy/src/main/scala/sbt/Configuration.scala create mode 100644 ivy/src/main/scala/sbt/ModuleID.scala create mode 100644 ivy/src/main/scala/sbt/Resolver.scala diff --git a/ivy/src/main/scala/sbt/Artifact.scala b/ivy/src/main/scala/sbt/Artifact.scala new file mode 100644 index 000000000..987a1f8ca --- /dev/null +++ b/ivy/src/main/scala/sbt/Artifact.scala @@ -0,0 +1,71 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package sbt + +import java.io.File +import java.net.URL + +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)) +} + + import Configurations.{config, Docs, Optional, Pom, Sources} + +object Artifact +{ + def apply(name: String): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None) + def apply(name: String, extra: Map[String,String]): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None, extra) + def apply(name: String, classifier: String): Artifact = Artifact(name, DefaultType, DefaultExtension, Some(classifier), Nil, None) + def apply(name: String, `type`: String, extension: String): Artifact = Artifact(name, `type`, extension, None, Nil, None) + def apply(name: String, `type`: String, extension: String, classifier: String): Artifact = Artifact(name, `type`, extension, Some(classifier), Nil, None) + def apply(name: String, url: URL): Artifact =Artifact(name, extract(url, DefaultType), extract(url, DefaultExtension), None, Nil, Some(url)) + def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL]): Artifact = + Artifact(name, `type`, extension, classifier, configurations, url, Map.empty) + + val DefaultExtension = "jar" + val DefaultType = "jar" + + def sources(name: String) = classified(name, SourceClassifier) + 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" + + def extract(url: URL, default: String): String = extract(url.toString, default) + def extract(name: String, default: String): String = + { + val i = name.lastIndexOf('.') + if(i >= 0) + name.substring(i+1) + else + default + } + def defaultArtifact(file: File) = + { + val name = file.getName + val i = name.lastIndexOf('.') + val base = if(i >= 0) name.substring(0, i) else name + Artifact(base, extract(name, DefaultType), extract(name, DefaultExtension), None, Nil, Some(file.toURI.toURL)) + } + def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String = + { + import artifact._ + val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c } + val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary) + val base = CrossVersion.applyCross(artifact.name, cross) + 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 = classifierConfMap.getOrElse(classifier, Optional) + def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix("test-"), DefaultType) + def classified(name: String, classifier: String): Artifact = + Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) +} diff --git a/ivy/src/main/scala/sbt/Configuration.scala b/ivy/src/main/scala/sbt/Configuration.scala new file mode 100644 index 000000000..a259f99d7 --- /dev/null +++ b/ivy/src/main/scala/sbt/Configuration.scala @@ -0,0 +1,65 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package sbt + +object Configurations +{ + def config(name: String) = new Configuration(name) + 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 names(cs: Seq[Configuration]) = cs.map(_.name) + + lazy val RuntimeInternal = optionalInternal(Runtime) + lazy val TestInternal = fullInternal(Test) + lazy val IntegrationTestInternal = fullInternal(IntegrationTest) + lazy val CompileInternal = fullInternal(Compile) + + def internalMap(c: Configuration) = c match { + case Compile => CompileInternal + case Test => TestInternal + case Runtime => RuntimeInternal + case IntegrationTest => IntegrationTestInternal + case _ => c + } + + def internal(base: Configuration, ext: Configuration*) = config(base.name + "-internal") extend(ext : _*) hide; + def fullInternal(base: Configuration): Configuration = internal(base, base, Optional, Provided) + def optionalInternal(base: Configuration): Configuration = internal(base, base, Optional) + + lazy val Default = config("default") + 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") + + lazy val ScalaTool = config("scala-tool") hide + lazy val CompilerPlugin = config("plugin") hide + + private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true) + private[sbt] val DefaultIvyConfiguration = defaultConfiguration(false) + private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration + private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) Configurations.Compile else Configurations.Default + private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*) +} +/** Represents an Ivy configuration. */ +final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean) +{ + require(name != null && !name.isEmpty) + require(description != null) + def this(name: String) = this(name, "", true, Nil, true) + def describedAs(newDescription: String) = Configuration(name, newDescription, isPublic, extendsConfigs, transitive) + def extend(configs: Configuration*) = Configuration(name, description, isPublic, configs.toList ::: extendsConfigs, transitive) + def notTransitive = intransitive + def intransitive = Configuration(name, description, isPublic, extendsConfigs, false) + def hide = Configuration(name, description, false, extendsConfigs, transitive) + override def toString = name +} diff --git a/ivy/src/main/scala/sbt/IvyInterface.scala b/ivy/src/main/scala/sbt/IvyInterface.scala index bd05f56c9..a1a651e55 100644 --- a/ivy/src/main/scala/sbt/IvyInterface.scala +++ b/ivy/src/main/scala/sbt/IvyInterface.scala @@ -9,101 +9,6 @@ import scala.xml.NodeSeq import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver} import org.apache.ivy.util.url.CredentialsStore -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) -{ - override def toString: String = - organization + ":" + name + ":" + revision + - (configurations match { case Some(s) => ":" + s; case None => "" }) + - (if(extraAttributes.isEmpty) "" else " " + extraString) - - /** String representation of the extra attributes, excluding any information only attributes. */ - def extraString: String = extraDependencyAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")") - - /** Returns the extra attributes except for ones marked as information only (ones that typically would not be used for dependency resolution). */ - def extraDependencyAttributes: Map[String,String] = extraAttributes.filterKeys(!_.startsWith(CustomPomParser.InfoKeyPrefix)) - - @deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0") - def cross(v: Boolean): ModuleID = cross(if(v) CrossVersion.binary else CrossVersion.Disabled) - - @deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0") - def cross(v: Boolean, verRemap: String => String): ModuleID = cross(if(v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled) - - /** Specifies the cross-version behavior for this module. See [CrossVersion] for details.*/ - def cross(v: CrossVersion): ModuleID = copy(crossVersion = v) - - // () required for chaining - /** Do not follow dependencies of this module. Synonym for `intransitive`.*/ - def notTransitive() = intransitive() - - /** Do not follow dependencies of this module. Synonym for `notTransitive`.*/ - def intransitive() = copy(isTransitive = false) - - /** Marks this dependency as "changing". Ivy will always check if the metadata has changed and then if the artifact has changed, - * redownload it. sbt configures all -SNAPSHOT dependencies to be changing. - * - * See the "Changes in artifacts" section of https://ant.apache.org/ivy/history/trunk/concept.html for full details. - * */ - def changing() = copy(isChanging = true) - - /** Indicates that conflict resolution should only select this module's revision. - * This prevents a newer revision from being pulled in by a transitive dependency, for example.*/ - def force() = copy(isForce = true) - - /** Specifies a URL from which the main artifact for this dependency can be downloaded. - * This value is only consulted if the module is not found in a repository. - * It is not included in published metadata.*/ - def from(url: String) = artifacts(Artifact(name, new URL(url))) - - /** Adds a dependency on the artifact for this module with classifier `c`. */ - def classifier(c: String) = artifacts(Artifact(name, c)) - - /** Declares the explicit artifacts for this module. If this ModuleID represents a dependency, - * these artifact definitions override the information in the dependency's published metadata. */ - def artifacts(newArtifacts: Artifact*) = copy(explicitArtifacts = newArtifacts ++ this.explicitArtifacts) - - /** 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) - - /** 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 extra(attributes: (String,String)*) = copy(extraAttributes = this.extraAttributes ++ ModuleID.checkE(attributes)) - - /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" - * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withSources()` instead.*/ - def sources() = artifacts(Artifact.sources(name)) - - /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" - * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withJavadoc()` instead.*/ - def javadoc() = artifacts(Artifact.javadoc(name)) - - def pomOnly() = artifacts(Artifact.pom(name)) - - /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" - * classifier. If there is not already an explicit dependency on the main artifact, this adds one.*/ - def withSources() = jarIfEmpty.sources() - - /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred - * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" - * classifier. If there is not already an explicit dependency on the main artifact, this adds one.*/ - def withJavadoc() = jarIfEmpty.javadoc() - - private def jarIfEmpty = if(explicitArtifacts.isEmpty) jar() else this - - /** Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such - * as when adding a dependency on an artifact with a classifier.*/ - def jar() = artifacts(Artifact(name)) -} -object ModuleID -{ - def checkE(attributes: Seq[(String, String)]) = - for ( (key, value) <- attributes) yield - if(key.startsWith("e:")) (key, value) else ("e:" + key, value) -} /** Additional information about a project module */ case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None) { @@ -112,414 +17,13 @@ case class ModuleInfo(nameFormal: String, description: String = "", homepage: Op def licensed(lics: (String, URL)*) = copy(licenses = lics) def organization(name: String, home: Option[URL]) = copy(organizationName = name, organizationHomepage = home) } + /** Basic SCM information for a project module */ case class ScmInfo(browseUrl: URL, connection: String, devConnection: Option[String] = None) + /** Rule to exclude unwanted dependencies pulled in transitively by a module. */ case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil) -sealed trait Resolver -{ - def name: String -} -final class RawRepository(val resolver: DependencyResolver) extends Resolver -{ - def name = resolver.getName - override def toString = "Raw(" + resolver.toString + ")" -} -sealed case class ChainedResolver(name: String, resolvers: Seq[Resolver]) extends Resolver -sealed case class MavenRepository(name: String, root: String) extends Resolver -{ - override def toString = name + ": " + root -} -final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean) -{ - private[sbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true) - private[sbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible) - private[sbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible) - override def toString = "Patterns(ivyPatterns=%s, artifactPatterns=%s, isMavenCompatible=%s)".format(ivyPatterns, artifactPatterns, isMavenCompatible) - override def equals(obj: Any): Boolean = { - obj match { - case other: Patterns => - ivyPatterns == other.ivyPatterns && artifactPatterns == other.artifactPatterns && isMavenCompatible == other.isMavenCompatible - case _ => false - } - } - override def hashCode: Int = (ivyPatterns, artifactPatterns, isMavenCompatible).hashCode -} -object Patterns -{ - implicit def defaultPatterns: Patterns = Resolver.defaultPatterns - - def apply(artifactPatterns: String*): Patterns = Patterns(true, artifactPatterns : _*) - def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = Patterns(artifactPatterns, artifactPatterns, isMavenCompatible) - def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean): Patterns = new Patterns(ivyPatterns, artifactPatterns, isMavenCompatible) -} -object RepositoryHelpers -{ - final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int]) - { - def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port) - } - /** Configuration specific to an Ivy filesystem resolver. */ - final case class FileConfiguration(isLocal: Boolean, isTransactional: Option[Boolean]) - { - def transactional() = FileConfiguration(isLocal, Some(true)) - def nontransactional() = FileConfiguration(isLocal, Some(false)) - def nonlocal() = FileConfiguration(false, isTransactional) - } - sealed trait SshAuthentication extends NotNull - final case class PasswordAuthentication(user: String, password: Option[String]) extends SshAuthentication - final case class KeyFileAuthentication(user: String, keyfile: File, password: Option[String]) extends SshAuthentication -} -import RepositoryHelpers.{SshConnection, FileConfiguration} -import RepositoryHelpers.{KeyFileAuthentication, PasswordAuthentication, SshAuthentication} - -/** sbt interface to an Ivy repository based on patterns, which is most Ivy repositories.*/ -sealed abstract class PatternsBasedRepository extends Resolver -{ - type RepositoryType <: PatternsBasedRepository - /** Should be implemented to create a new copy of this repository but with `patterns` as given.*/ - protected def copy(patterns: Patterns): RepositoryType - - /** The object representing the configured patterns for this repository. */ - def patterns: Patterns - - /** Enables maven 2 compatibility for this repository. */ - def mavenStyle() = copy(patterns.mavenStyle()) - /** Adds the given patterns for resolving/publishing Ivy files.*/ - def ivys(ivyPatterns: String*): RepositoryType = copy(patterns.withIvys(ivyPatterns)) - /** Adds the given patterns for resolving/publishing artifacts.*/ - def artifacts(artifactPatterns: String*): RepositoryType = copy(patterns.withArtifacts(artifactPatterns)) -} -/** sbt interface for an Ivy filesystem repository. More convenient construction is done using Resolver.file. */ -final case class FileRepository(name: String, configuration: FileConfiguration, patterns: Patterns) extends PatternsBasedRepository -{ - type RepositoryType = FileRepository - protected def copy(patterns: Patterns): FileRepository = FileRepository(name, configuration, patterns) - private def copy(configuration: FileConfiguration) = FileRepository(name, configuration, patterns) - def transactional() = copy(configuration.transactional()) - def nonlocal() = copy(configuration.nonlocal()) -} -final case class URLRepository(name: String, patterns: Patterns) extends PatternsBasedRepository -{ - type RepositoryType = URLRepository - protected def copy(patterns: Patterns): URLRepository = URLRepository(name, patterns) -} -/** sbt interface for an Ivy ssh-based repository (ssh and sftp). Requires the Jsch library.. */ -sealed abstract class SshBasedRepository extends PatternsBasedRepository -{ - type RepositoryType <: SshBasedRepository - protected def copy(connection: SshConnection): RepositoryType - private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication))) - - /** The object representing the configured ssh connection for this repository. */ - def connection: SshConnection - - /** Configures this to use the specified user name and password when connecting to the remote repository. */ - def as(user: String, password: String): RepositoryType = as(user, Some(password)) - def as(user: String): RepositoryType = as(user, None) - def as(user: String, password: Option[String]) = copy(new PasswordAuthentication(user, password)) - /** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */ - def as(user: String, keyfile: File): RepositoryType = as(user, keyfile, None) - def as(user: String, keyfile: File, password: String): RepositoryType = as(user, keyfile, Some(password)) - def as(user: String, keyfile: File, password: Option[String]): RepositoryType = copy(new KeyFileAuthentication(user, keyfile, password)) -} -/** sbt interface for an Ivy repository over ssh. More convenient construction is done using Resolver.ssh. */ -final case class SshRepository(name: String, connection: SshConnection, patterns: Patterns, publishPermissions: Option[String]) extends SshBasedRepository -{ - type RepositoryType = SshRepository - protected def copy(patterns: Patterns): SshRepository = SshRepository(name, connection, patterns, publishPermissions) - protected def copy(connection: SshConnection): SshRepository = SshRepository(name, connection, patterns, publishPermissions) - /** Defines the permissions to set when publishing to this repository. */ - def withPermissions(publishPermissions: String): SshRepository = withPermissions(Some(publishPermissions)) - def withPermissions(publishPermissions: Option[String]): SshRepository = SshRepository(name, connection, patterns, publishPermissions) -} -/** sbt interface for an Ivy repository over sftp. More convenient construction is done using Resolver.sftp. */ -final case class SftpRepository(name: String, connection: SshConnection, patterns: Patterns) extends SshBasedRepository -{ - type RepositoryType = SftpRepository - protected def copy(patterns: Patterns): SftpRepository = SftpRepository(name, connection, patterns) - protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns) -} - -import Resolver._ -@deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") -object ScalaToolsReleases extends MavenRepository(ScalaToolsReleasesName, ScalaToolsReleasesRoot) -@deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") -object ScalaToolsSnapshots extends MavenRepository(ScalaToolsSnapshotsName, ScalaToolsSnapshotsRoot) -object DefaultMavenRepository extends MavenRepository("public", IBiblioResolver.DEFAULT_M2_ROOT) -object JavaNet2Repository extends MavenRepository(JavaNet2RepositoryName, JavaNet2RepositoryRoot) -object JavaNet1Repository extends JavaNet1Repository -sealed trait JavaNet1Repository extends Resolver -{ - def name = "java.net Maven1 Repository" -} - -object Resolver -{ - val TypesafeRepositoryRoot = "http://repo.typesafe.com/typesafe" - val SbtPluginRepositoryRoot = "http://scalasbt.artifactoryonline.com/scalasbt" - val SonatypeRepositoryRoot = "https://oss.sonatype.org/content/repositories" - @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") - val ScalaToolsReleasesName = "Sonatype OSS Releases" - @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") - val ScalaToolsSnapshotsName = "Sonatype OSS Snapshots" - @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") - val ScalaToolsReleasesRoot = SonatypeRepositoryRoot + "/releases" - @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") - val ScalaToolsSnapshotsRoot = SonatypeRepositoryRoot + "/snapshots" - val JavaNet2RepositoryName = "java.net Maven2 Repository" - val JavaNet2RepositoryRoot = "http://download.java.net/maven/2" - - def typesafeRepo(status: String) = new MavenRepository("typesafe-" + status, TypesafeRepositoryRoot + "/" + status) - def typesafeIvyRepo(status: String) = url("typesafe-ivy-" + status, new URL(TypesafeRepositoryRoot + "/ivy-" + status + "/"))(ivyStylePatterns) - def sbtPluginRepo(status: String) = url("sbt-plugin-" + status, new URL(SbtPluginRepositoryRoot + "/sbt-plugin-" + status + "/"))(ivyStylePatterns) - def sonatypeRepo(status: String) = new MavenRepository("sonatype-" + status, SonatypeRepositoryRoot + "/" + status) - - /** Add the local and Maven Central repositories to the user repositories. */ - def withDefaultResolvers(userResolvers: Seq[Resolver]): Seq[Resolver] = - withDefaultResolvers(userResolvers, true) - /** Add the local Ivy repository to the user repositories. - * If `mavenCentral` is true, add the Maven Central repository. */ - def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean): Seq[Resolver] = - Seq(Resolver.defaultLocal) ++ - userResolvers ++ - single(DefaultMavenRepository, mavenCentral) - private def single[T](value: T, nonEmpty: Boolean): Seq[T] = if(nonEmpty) Seq(value) else Nil - - /** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */ - sealed abstract class Define[RepositoryType <: SshBasedRepository] extends NotNull - { - /** Subclasses should implement this method to */ - protected def construct(name: String, connection: SshConnection, patterns: Patterns): RepositoryType - /** Constructs this repository type with the given `name`. `basePatterns` are the initial patterns to use. A ManagedProject - * has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ - def apply(name: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, None, None, None) - /** Constructs this repository type with the given `name` and `hostname`. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ - def apply(name: String, hostname: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), None, None) - /** Constructs this repository type with the given `name`, `hostname`, and the `basePath` against which the initial - * patterns will be resolved. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ - def apply(name: String, hostname: String, basePath: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), None, Some(basePath)) - /** Constructs this repository type with the given `name`, `hostname`, and `port`. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ - def apply(name: String, hostname: String, port: Int)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), Some(port), None) - /** Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial - * patterns will be resolved. `basePatterns` are the initial patterns to use. - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ - def apply(name: String, hostname: String, port: Int, basePath: String)(implicit basePatterns: Patterns): RepositoryType = - apply(name, Some(hostname), Some(port), Some(basePath)) - /** Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial - * patterns will be resolved. `basePatterns` are the initial patterns to use. All but the `name` are optional (use None). - * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ - def apply(name: String, hostname: Option[String], port: Option[Int], basePath: Option[String])(implicit basePatterns: Patterns): RepositoryType = - construct(name, SshConnection(None, hostname, port), resolvePatterns(basePath, basePatterns)) - } - /** A factory to construct an interface to an Ivy SSH resolver.*/ - object ssh extends Define[SshRepository] - { - protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None) - } - /** A factory to construct an interface to an Ivy SFTP resolver.*/ - object sftp extends Define[SftpRepository] - { - protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns) - } - /** A factory to construct an interface to an Ivy filesytem resolver. */ - object file - { - /** Constructs a file resolver with the given name. The patterns to use must be explicitly specified - * using the `ivys` or `artifacts` methods on the constructed resolver object.*/ - def apply(name: String): FileRepository = FileRepository(name, defaultFileConfiguration, Patterns(false)) - /** Constructs a file resolver with the given name and base directory. */ - def apply(name: String, baseDirectory: File)(implicit basePatterns: Patterns): FileRepository = - baseRepository(new File(baseDirectory.toURI.normalize) getAbsolutePath)(FileRepository(name, defaultFileConfiguration, _)) - } - object url - { - /** Constructs a URL resolver with the given name. The patterns to use must be explicitly specified - * using the `ivys` or `artifacts` methods on the constructed resolver object.*/ - def apply(name: String): URLRepository = URLRepository(name, Patterns(false)) - /** Constructs a file resolver with the given name and base directory. */ - def apply(name: String, baseURL: URL)(implicit basePatterns: Patterns): URLRepository = - baseRepository(baseURL.toURI.normalize.toString)(URLRepository(name, _)) - } - private def baseRepository[T](base: String)(construct: Patterns => T)(implicit basePatterns: Patterns): T = - construct(resolvePatterns(base, basePatterns)) - - /** If `base` is None, `patterns` is returned unchanged. - * Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. */ - private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns = - base match - { - case Some(path) => resolvePatterns(path, patterns) - case None => patterns - } - /** Resolves the ivy file and artifact patterns in `patterns` against the given base. */ - private def resolvePatterns(base: String, basePatterns: Patterns): Patterns = - { - def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p)) - Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible) - } - private[sbt] def resolvePattern(base: String, pattern: String): String = - { - val normBase = base.replace('\\', '/') - if(normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern - } - def defaultFileConfiguration = FileConfiguration(true, None) - def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true) - def ivyStylePatterns = defaultIvyPatterns//Patterns(Nil, Nil, false) - - def defaultPatterns = mavenStylePatterns - def mavenStyleBasePattern = "[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]" - def localBasePattern = "[organisation]/[module]/" + PluginPattern + "[revision]/[type]s/[artifact](-[classifier]).[ext]" - def defaultRetrievePattern = "[type]s/[organisation]/[module]/" + PluginPattern + "[artifact](-[revision])(-[classifier]).[ext]" - final val PluginPattern = "(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" - - def mavenLocal = MavenRepository("Maven2 Local", (new File(Path.userHome, ".m2/repository/")).toURI.toURL.toExternalForm) - def defaultLocal = defaultUserFileRepository("local") - def defaultShared = defaultUserFileRepository("shared") - def defaultUserFileRepository(id: String) = - { - val pList = ("${ivy.home}/" + id + "/" + localBasePattern) :: Nil - FileRepository(id, defaultFileConfiguration, Patterns(pList, pList, false)) - } - def defaultIvyPatterns = - { - val pList = List(localBasePattern) - Patterns(pList, pList, false) - } -} - -object Configurations -{ - def config(name: String) = new Configuration(name) - 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 names(cs: Seq[Configuration]) = cs.map(_.name) - - lazy val RuntimeInternal = optionalInternal(Runtime) - lazy val TestInternal = fullInternal(Test) - lazy val IntegrationTestInternal = fullInternal(IntegrationTest) - lazy val CompileInternal = fullInternal(Compile) - - def internalMap(c: Configuration) = c match { - case Compile => CompileInternal - case Test => TestInternal - case Runtime => RuntimeInternal - case IntegrationTest => IntegrationTestInternal - case _ => c - } - - def internal(base: Configuration, ext: Configuration*) = config(base.name + "-internal") extend(ext : _*) hide; - def fullInternal(base: Configuration): Configuration = internal(base, base, Optional, Provided) - def optionalInternal(base: Configuration): Configuration = internal(base, base, Optional) - - lazy val Default = config("default") - 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") - - lazy val ScalaTool = config("scala-tool") hide - lazy val CompilerPlugin = config("plugin") hide - - private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true) - private[sbt] val DefaultIvyConfiguration = defaultConfiguration(false) - private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration - private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) Configurations.Compile else Configurations.Default - private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*) -} -/** Represents an Ivy configuration. */ -final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean) -{ - require(name != null && !name.isEmpty) - require(description != null) - def this(name: String) = this(name, "", true, Nil, true) - def describedAs(newDescription: String) = Configuration(name, newDescription, isPublic, extendsConfigs, transitive) - def extend(configs: Configuration*) = Configuration(name, description, isPublic, configs.toList ::: extendsConfigs, transitive) - def notTransitive = intransitive - def intransitive = Configuration(name, description, isPublic, extendsConfigs, false) - def hide = Configuration(name, description, false, extendsConfigs, transitive) - override def toString = name -} - -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)) -} - - import Configurations.{config, Docs, Optional, Pom, Sources} - -object Artifact -{ - def apply(name: String): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None) - def apply(name: String, extra: Map[String,String]): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None, extra) - def apply(name: String, classifier: String): Artifact = Artifact(name, DefaultType, DefaultExtension, Some(classifier), Nil, None) - def apply(name: String, `type`: String, extension: String): Artifact = Artifact(name, `type`, extension, None, Nil, None) - def apply(name: String, `type`: String, extension: String, classifier: String): Artifact = Artifact(name, `type`, extension, Some(classifier), Nil, None) - def apply(name: String, url: URL): Artifact =Artifact(name, extract(url, DefaultType), extract(url, DefaultExtension), None, Nil, Some(url)) - def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL]): Artifact = - Artifact(name, `type`, extension, classifier, configurations, url, Map.empty) - - val DefaultExtension = "jar" - val DefaultType = "jar" - - def sources(name: String) = classified(name, SourceClassifier) - 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" - - def extract(url: URL, default: String): String = extract(url.toString, default) - def extract(name: String, default: String): String = - { - val i = name.lastIndexOf('.') - if(i >= 0) - name.substring(i+1) - else - default - } - def defaultArtifact(file: File) = - { - val name = file.getName - val i = name.lastIndexOf('.') - val base = if(i >= 0) name.substring(0, i) else name - Artifact(base, extract(name, DefaultType), extract(name, DefaultExtension), None, Nil, Some(file.toURI.toURL)) - } - def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String = - { - import artifact._ - val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c } - val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary) - val base = CrossVersion.applyCross(artifact.name, cross) - 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 = classifierConfMap.getOrElse(classifier, Optional) - def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix("test-"), DefaultType) - def classified(name: String, classifier: String): Artifact = - Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) -} final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver) object ModuleConfiguration { diff --git a/ivy/src/main/scala/sbt/ModuleID.scala b/ivy/src/main/scala/sbt/ModuleID.scala new file mode 100644 index 000000000..df69274d4 --- /dev/null +++ b/ivy/src/main/scala/sbt/ModuleID.scala @@ -0,0 +1,102 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package sbt + + import java.net.URL + +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) +{ + override def toString: String = + organization + ":" + name + ":" + revision + + (configurations match { case Some(s) => ":" + s; case None => "" }) + + (if(extraAttributes.isEmpty) "" else " " + extraString) + + /** String representation of the extra attributes, excluding any information only attributes. */ + def extraString: String = extraDependencyAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")") + + /** Returns the extra attributes except for ones marked as information only (ones that typically would not be used for dependency resolution). */ + def extraDependencyAttributes: Map[String,String] = extraAttributes.filterKeys(!_.startsWith(CustomPomParser.InfoKeyPrefix)) + + @deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0") + def cross(v: Boolean): ModuleID = cross(if(v) CrossVersion.binary else CrossVersion.Disabled) + + @deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0") + def cross(v: Boolean, verRemap: String => String): ModuleID = cross(if(v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled) + + /** Specifies the cross-version behavior for this module. See [CrossVersion] for details.*/ + def cross(v: CrossVersion): ModuleID = copy(crossVersion = v) + + // () required for chaining + /** Do not follow dependencies of this module. Synonym for `intransitive`.*/ + def notTransitive() = intransitive() + + /** Do not follow dependencies of this module. Synonym for `notTransitive`.*/ + def intransitive() = copy(isTransitive = false) + + /** Marks this dependency as "changing". Ivy will always check if the metadata has changed and then if the artifact has changed, + * redownload it. sbt configures all -SNAPSHOT dependencies to be changing. + * + * See the "Changes in artifacts" section of https://ant.apache.org/ivy/history/trunk/concept.html for full details. + * */ + def changing() = copy(isChanging = true) + + /** Indicates that conflict resolution should only select this module's revision. + * This prevents a newer revision from being pulled in by a transitive dependency, for example.*/ + def force() = copy(isForce = true) + + /** Specifies a URL from which the main artifact for this dependency can be downloaded. + * This value is only consulted if the module is not found in a repository. + * It is not included in published metadata.*/ + def from(url: String) = artifacts(Artifact(name, new URL(url))) + + /** Adds a dependency on the artifact for this module with classifier `c`. */ + def classifier(c: String) = artifacts(Artifact(name, c)) + + /** Declares the explicit artifacts for this module. If this ModuleID represents a dependency, + * these artifact definitions override the information in the dependency's published metadata. */ + def artifacts(newArtifacts: Artifact*) = copy(explicitArtifacts = newArtifacts ++ this.explicitArtifacts) + + /** 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) + + /** 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 extra(attributes: (String,String)*) = copy(extraAttributes = this.extraAttributes ++ ModuleID.checkE(attributes)) + + /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred + * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" + * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withSources()` instead.*/ + def sources() = artifacts(Artifact.sources(name)) + + /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred + * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" + * classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withJavadoc()` instead.*/ + def javadoc() = artifacts(Artifact.javadoc(name)) + + def pomOnly() = artifacts(Artifact.pom(name)) + + /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred + * for performance and correctness. This method adds a dependency on this module's artifact with the "sources" + * classifier. If there is not already an explicit dependency on the main artifact, this adds one.*/ + def withSources() = jarIfEmpty.sources() + + /** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred + * for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc" + * classifier. If there is not already an explicit dependency on the main artifact, this adds one.*/ + def withJavadoc() = jarIfEmpty.javadoc() + + private def jarIfEmpty = if(explicitArtifacts.isEmpty) jar() else this + + /** Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such + * as when adding a dependency on an artifact with a classifier.*/ + def jar() = artifacts(Artifact(name)) +} +object ModuleID +{ + def checkE(attributes: Seq[(String, String)]) = + for ( (key, value) <- attributes) yield + if(key.startsWith("e:")) (key, value) else ("e:" + key, value) +} diff --git a/ivy/src/main/scala/sbt/Resolver.scala b/ivy/src/main/scala/sbt/Resolver.scala new file mode 100644 index 000000000..e315944da --- /dev/null +++ b/ivy/src/main/scala/sbt/Resolver.scala @@ -0,0 +1,289 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package sbt + +import java.io.File +import java.net.URL +import scala.xml.NodeSeq +import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver} + +sealed trait Resolver +{ + def name: String +} +final class RawRepository(val resolver: DependencyResolver) extends Resolver +{ + def name = resolver.getName + override def toString = "Raw(" + resolver.toString + ")" +} +sealed case class ChainedResolver(name: String, resolvers: Seq[Resolver]) extends Resolver +sealed case class MavenRepository(name: String, root: String) extends Resolver +{ + override def toString = name + ": " + root +} + +final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean) +{ + private[sbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true) + private[sbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible) + private[sbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible) + override def toString = "Patterns(ivyPatterns=%s, artifactPatterns=%s, isMavenCompatible=%s)".format(ivyPatterns, artifactPatterns, isMavenCompatible) + override def equals(obj: Any): Boolean = { + obj match { + case other: Patterns => + ivyPatterns == other.ivyPatterns && artifactPatterns == other.artifactPatterns && isMavenCompatible == other.isMavenCompatible + case _ => false + } + } + override def hashCode: Int = (ivyPatterns, artifactPatterns, isMavenCompatible).hashCode +} +object Patterns +{ + implicit def defaultPatterns: Patterns = Resolver.defaultPatterns + + def apply(artifactPatterns: String*): Patterns = Patterns(true, artifactPatterns : _*) + def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = Patterns(artifactPatterns, artifactPatterns, isMavenCompatible) + def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean): Patterns = new Patterns(ivyPatterns, artifactPatterns, isMavenCompatible) +} +object RepositoryHelpers +{ + final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int]) + { + def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port) + } + /** Configuration specific to an Ivy filesystem resolver. */ + final case class FileConfiguration(isLocal: Boolean, isTransactional: Option[Boolean]) + { + def transactional() = FileConfiguration(isLocal, Some(true)) + def nontransactional() = FileConfiguration(isLocal, Some(false)) + def nonlocal() = FileConfiguration(false, isTransactional) + } + sealed trait SshAuthentication extends NotNull + final case class PasswordAuthentication(user: String, password: Option[String]) extends SshAuthentication + final case class KeyFileAuthentication(user: String, keyfile: File, password: Option[String]) extends SshAuthentication +} +import RepositoryHelpers.{SshConnection, FileConfiguration} +import RepositoryHelpers.{KeyFileAuthentication, PasswordAuthentication, SshAuthentication} + +/** sbt interface to an Ivy repository based on patterns, which is most Ivy repositories.*/ +sealed abstract class PatternsBasedRepository extends Resolver +{ + type RepositoryType <: PatternsBasedRepository + /** Should be implemented to create a new copy of this repository but with `patterns` as given.*/ + protected def copy(patterns: Patterns): RepositoryType + + /** The object representing the configured patterns for this repository. */ + def patterns: Patterns + + /** Enables maven 2 compatibility for this repository. */ + def mavenStyle() = copy(patterns.mavenStyle()) + /** Adds the given patterns for resolving/publishing Ivy files.*/ + def ivys(ivyPatterns: String*): RepositoryType = copy(patterns.withIvys(ivyPatterns)) + /** Adds the given patterns for resolving/publishing artifacts.*/ + def artifacts(artifactPatterns: String*): RepositoryType = copy(patterns.withArtifacts(artifactPatterns)) +} +/** sbt interface for an Ivy filesystem repository. More convenient construction is done using Resolver.file. */ +final case class FileRepository(name: String, configuration: FileConfiguration, patterns: Patterns) extends PatternsBasedRepository +{ + type RepositoryType = FileRepository + protected def copy(patterns: Patterns): FileRepository = FileRepository(name, configuration, patterns) + private def copy(configuration: FileConfiguration) = FileRepository(name, configuration, patterns) + def transactional() = copy(configuration.transactional()) + def nonlocal() = copy(configuration.nonlocal()) +} +final case class URLRepository(name: String, patterns: Patterns) extends PatternsBasedRepository +{ + type RepositoryType = URLRepository + protected def copy(patterns: Patterns): URLRepository = URLRepository(name, patterns) +} +/** sbt interface for an Ivy ssh-based repository (ssh and sftp). Requires the Jsch library.. */ +sealed abstract class SshBasedRepository extends PatternsBasedRepository +{ + type RepositoryType <: SshBasedRepository + protected def copy(connection: SshConnection): RepositoryType + private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication))) + + /** The object representing the configured ssh connection for this repository. */ + def connection: SshConnection + + /** Configures this to use the specified user name and password when connecting to the remote repository. */ + def as(user: String, password: String): RepositoryType = as(user, Some(password)) + def as(user: String): RepositoryType = as(user, None) + def as(user: String, password: Option[String]) = copy(new PasswordAuthentication(user, password)) + /** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */ + def as(user: String, keyfile: File): RepositoryType = as(user, keyfile, None) + def as(user: String, keyfile: File, password: String): RepositoryType = as(user, keyfile, Some(password)) + def as(user: String, keyfile: File, password: Option[String]): RepositoryType = copy(new KeyFileAuthentication(user, keyfile, password)) +} +/** sbt interface for an Ivy repository over ssh. More convenient construction is done using Resolver.ssh. */ +final case class SshRepository(name: String, connection: SshConnection, patterns: Patterns, publishPermissions: Option[String]) extends SshBasedRepository +{ + type RepositoryType = SshRepository + protected def copy(patterns: Patterns): SshRepository = SshRepository(name, connection, patterns, publishPermissions) + protected def copy(connection: SshConnection): SshRepository = SshRepository(name, connection, patterns, publishPermissions) + /** Defines the permissions to set when publishing to this repository. */ + def withPermissions(publishPermissions: String): SshRepository = withPermissions(Some(publishPermissions)) + def withPermissions(publishPermissions: Option[String]): SshRepository = SshRepository(name, connection, patterns, publishPermissions) +} +/** sbt interface for an Ivy repository over sftp. More convenient construction is done using Resolver.sftp. */ +final case class SftpRepository(name: String, connection: SshConnection, patterns: Patterns) extends SshBasedRepository +{ + type RepositoryType = SftpRepository + protected def copy(patterns: Patterns): SftpRepository = SftpRepository(name, connection, patterns) + protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns) +} + +import Resolver._ +@deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") +object ScalaToolsReleases extends MavenRepository(ScalaToolsReleasesName, ScalaToolsReleasesRoot) +@deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") +object ScalaToolsSnapshots extends MavenRepository(ScalaToolsSnapshotsName, ScalaToolsSnapshotsRoot) +object DefaultMavenRepository extends MavenRepository("public", IBiblioResolver.DEFAULT_M2_ROOT) +object JavaNet2Repository extends MavenRepository(JavaNet2RepositoryName, JavaNet2RepositoryRoot) +object JavaNet1Repository extends JavaNet1Repository +sealed trait JavaNet1Repository extends Resolver +{ + def name = "java.net Maven1 Repository" +} + +object Resolver +{ + val TypesafeRepositoryRoot = "http://repo.typesafe.com/typesafe" + val SbtPluginRepositoryRoot = "http://scalasbt.artifactoryonline.com/scalasbt" + val SonatypeRepositoryRoot = "https://oss.sonatype.org/content/repositories" + @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") + val ScalaToolsReleasesName = "Sonatype OSS Releases" + @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") + val ScalaToolsSnapshotsName = "Sonatype OSS Snapshots" + @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") + val ScalaToolsReleasesRoot = SonatypeRepositoryRoot + "/releases" + @deprecated("Maven repository at scala-tools.org is being phased out, consider using an alternative resolver", "0.11.3") + val ScalaToolsSnapshotsRoot = SonatypeRepositoryRoot + "/snapshots" + val JavaNet2RepositoryName = "java.net Maven2 Repository" + val JavaNet2RepositoryRoot = "http://download.java.net/maven/2" + + def typesafeRepo(status: String) = new MavenRepository("typesafe-" + status, TypesafeRepositoryRoot + "/" + status) + def typesafeIvyRepo(status: String) = url("typesafe-ivy-" + status, new URL(TypesafeRepositoryRoot + "/ivy-" + status + "/"))(ivyStylePatterns) + def sbtPluginRepo(status: String) = url("sbt-plugin-" + status, new URL(SbtPluginRepositoryRoot + "/sbt-plugin-" + status + "/"))(ivyStylePatterns) + def sonatypeRepo(status: String) = new MavenRepository("sonatype-" + status, SonatypeRepositoryRoot + "/" + status) + + /** Add the local and Maven Central repositories to the user repositories. */ + def withDefaultResolvers(userResolvers: Seq[Resolver]): Seq[Resolver] = + withDefaultResolvers(userResolvers, true) + /** Add the local Ivy repository to the user repositories. + * If `mavenCentral` is true, add the Maven Central repository. */ + def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean): Seq[Resolver] = + Seq(Resolver.defaultLocal) ++ + userResolvers ++ + single(DefaultMavenRepository, mavenCentral) + private def single[T](value: T, nonEmpty: Boolean): Seq[T] = if(nonEmpty) Seq(value) else Nil + + /** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */ + sealed abstract class Define[RepositoryType <: SshBasedRepository] extends NotNull + { + /** Subclasses should implement this method to */ + protected def construct(name: String, connection: SshConnection, patterns: Patterns): RepositoryType + /** Constructs this repository type with the given `name`. `basePatterns` are the initial patterns to use. A ManagedProject + * has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ + def apply(name: String)(implicit basePatterns: Patterns): RepositoryType = + apply(name, None, None, None) + /** Constructs this repository type with the given `name` and `hostname`. `basePatterns` are the initial patterns to use. + * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ + def apply(name: String, hostname: String)(implicit basePatterns: Patterns): RepositoryType = + apply(name, Some(hostname), None, None) + /** Constructs this repository type with the given `name`, `hostname`, and the `basePath` against which the initial + * patterns will be resolved. `basePatterns` are the initial patterns to use. + * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ + def apply(name: String, hostname: String, basePath: String)(implicit basePatterns: Patterns): RepositoryType = + apply(name, Some(hostname), None, Some(basePath)) + /** Constructs this repository type with the given `name`, `hostname`, and `port`. `basePatterns` are the initial patterns to use. + * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ + def apply(name: String, hostname: String, port: Int)(implicit basePatterns: Patterns): RepositoryType = + apply(name, Some(hostname), Some(port), None) + /** Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial + * patterns will be resolved. `basePatterns` are the initial patterns to use. + * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ + def apply(name: String, hostname: String, port: Int, basePath: String)(implicit basePatterns: Patterns): RepositoryType = + apply(name, Some(hostname), Some(port), Some(basePath)) + /** Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial + * patterns will be resolved. `basePatterns` are the initial patterns to use. All but the `name` are optional (use None). + * A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/ + def apply(name: String, hostname: Option[String], port: Option[Int], basePath: Option[String])(implicit basePatterns: Patterns): RepositoryType = + construct(name, SshConnection(None, hostname, port), resolvePatterns(basePath, basePatterns)) + } + /** A factory to construct an interface to an Ivy SSH resolver.*/ + object ssh extends Define[SshRepository] + { + protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None) + } + /** A factory to construct an interface to an Ivy SFTP resolver.*/ + object sftp extends Define[SftpRepository] + { + protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns) + } + /** A factory to construct an interface to an Ivy filesytem resolver. */ + object file + { + /** Constructs a file resolver with the given name. The patterns to use must be explicitly specified + * using the `ivys` or `artifacts` methods on the constructed resolver object.*/ + def apply(name: String): FileRepository = FileRepository(name, defaultFileConfiguration, Patterns(false)) + /** Constructs a file resolver with the given name and base directory. */ + def apply(name: String, baseDirectory: File)(implicit basePatterns: Patterns): FileRepository = + baseRepository(new File(baseDirectory.toURI.normalize) getAbsolutePath)(FileRepository(name, defaultFileConfiguration, _)) + } + object url + { + /** Constructs a URL resolver with the given name. The patterns to use must be explicitly specified + * using the `ivys` or `artifacts` methods on the constructed resolver object.*/ + def apply(name: String): URLRepository = URLRepository(name, Patterns(false)) + /** Constructs a file resolver with the given name and base directory. */ + def apply(name: String, baseURL: URL)(implicit basePatterns: Patterns): URLRepository = + baseRepository(baseURL.toURI.normalize.toString)(URLRepository(name, _)) + } + private def baseRepository[T](base: String)(construct: Patterns => T)(implicit basePatterns: Patterns): T = + construct(resolvePatterns(base, basePatterns)) + + /** If `base` is None, `patterns` is returned unchanged. + * Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. */ + private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns = + base match + { + case Some(path) => resolvePatterns(path, patterns) + case None => patterns + } + /** Resolves the ivy file and artifact patterns in `patterns` against the given base. */ + private def resolvePatterns(base: String, basePatterns: Patterns): Patterns = + { + def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p)) + Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible) + } + private[sbt] def resolvePattern(base: String, pattern: String): String = + { + val normBase = base.replace('\\', '/') + if(normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern + } + def defaultFileConfiguration = FileConfiguration(true, None) + def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true) + def ivyStylePatterns = defaultIvyPatterns//Patterns(Nil, Nil, false) + + def defaultPatterns = mavenStylePatterns + def mavenStyleBasePattern = "[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]" + def localBasePattern = "[organisation]/[module]/" + PluginPattern + "[revision]/[type]s/[artifact](-[classifier]).[ext]" + def defaultRetrievePattern = "[type]s/[organisation]/[module]/" + PluginPattern + "[artifact](-[revision])(-[classifier]).[ext]" + final val PluginPattern = "(scala_[scalaVersion]/)(sbt_[sbtVersion]/)" + + def mavenLocal = MavenRepository("Maven2 Local", (new File(Path.userHome, ".m2/repository/")).toURI.toURL.toExternalForm) + def defaultLocal = defaultUserFileRepository("local") + def defaultShared = defaultUserFileRepository("shared") + def defaultUserFileRepository(id: String) = + { + val pList = ("${ivy.home}/" + id + "/" + localBasePattern) :: Nil + FileRepository(id, defaultFileConfiguration, Patterns(pList, pList, false)) + } + def defaultIvyPatterns = + { + val pList = List(localBasePattern) + Patterns(pList, pList, false) + } +}