package sbt
import java.io.File
import java.net.{URI, URL}
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 =
organization + ":" + name + ":" + revision +
(configurations match { case Some(s) => ":" + s; case None => "" }) +
(if(extraAttributes.isEmpty) "" else " " + extraString)
def extraString = extraAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")")
@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)
def cross(v: CrossVersion): ModuleID = copy(crossVersion = v)
def notTransitive() = intransitive()
def intransitive() = copy(isTransitive = false)
def changing() = copy(isChanging = true)
def force() = copy(isForce = true)
def from(url: String) = artifacts(Artifact(name, new URL(url)))
def classifier(c: String) = artifacts(Artifact(name, c))
def artifacts(newArtifacts: Artifact*) = copy(explicitArtifacts = newArtifacts ++ this.explicitArtifacts)
def excludeAll(rules: ExclusionRule*) = copy(exclusions = this.exclusions ++ rules)
def exclude(org: String, name: String) = excludeAll(ExclusionRule(org, name))
def extra(attributes: (String,String)*) = copy(extraAttributes = this.extraAttributes ++ ModuleID.checkE(attributes))
def sources() = artifacts(Artifact.sources(name))
def javadoc() = artifacts(Artifact.javadoc(name))
def withSources() = jarIfEmpty.sources()
def withJavadoc() = jarIfEmpty.javadoc()
private def jarIfEmpty = if(explicitArtifacts.isEmpty) jar() else this
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)
}
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)
{
def formally(name: String) = copy(nameFormal = name)
def describing(desc: String, home: Option[URL]) = copy(description = desc, homepage = home)
def licensed(lics: (String, URL)*) = copy(licenses = lics)
def organization(name: String, home: Option[URL]) = copy(organizationName = name, organizationHomepage = home)
}
case class ScmInfo(browseUrl: URL, connection: String, devConnection: Option[String] = None)
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)
}
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)
}
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}
sealed abstract class PatternsBasedRepository extends Resolver
{
type RepositoryType <: PatternsBasedRepository
protected def copy(patterns: Patterns): RepositoryType
def patterns: Patterns
def mavenStyle() = copy(patterns.mavenStyle())
def ivys(ivyPatterns: String*): RepositoryType = copy(patterns.withIvys(ivyPatterns))
def artifacts(artifactPatterns: String*): RepositoryType = copy(patterns.withArtifacts(artifactPatterns))
}
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)
}
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)))
def connection: SshConnection
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))
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))
}
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)
def withPermissions(publishPermissions: String): SshRepository = withPermissions(Some(publishPermissions))
def withPermissions(publishPermissions: Option[String]): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
}
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)
def withDefaultResolvers(userResolvers: Seq[Resolver]): Seq[Resolver] =
withDefaultResolvers(userResolvers, true)
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
sealed abstract class Define[RepositoryType <: SshBasedRepository] extends NotNull
{
protected def construct(name: String, connection: SshConnection, patterns: Patterns): RepositoryType
def apply(name: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, None, None, None)
def apply(name: String, hostname: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), None, None)
def apply(name: String, hostname: String, basePath: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), None, Some(basePath))
def apply(name: String, hostname: String, port: Int)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), Some(port), None)
def apply(name: String, hostname: String, port: Int, basePath: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), Some(port), Some(basePath))
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))
}
object ssh extends Define[SshRepository]
{
protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None)
}
object sftp extends Define[SftpRepository]
{
protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns)
}
object file
{
def apply(name: String): FileRepository = FileRepository(name, defaultFileConfiguration, Patterns(false))
def apply(name: String, baseDirectory: File)(implicit basePatterns: Patterns): FileRepository =
baseRepository(new File(baseDirectory.toURI.normalize) getAbsolutePath)(FileRepository(name, defaultFileConfiguration, _))
}
object url
{
def apply(name: String): URLRepository = URLRepository(name, Patterns(false))
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))
private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns =
base match
{
case Some(path) => resolvePatterns(path, patterns)
case None => patterns
}
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
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 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: _*)
}
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
{
def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver)
def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver)
}