Merge pull request #62 from alexarchambault/develop

Last developments
This commit is contained in:
Alexandre Archambault 2015-07-07 14:21:54 +02:00
commit fffc3dd058
9 changed files with 322 additions and 56 deletions

View File

@ -53,7 +53,7 @@ case class Coursier(
println(s"Downloading $url")
def downloaded(url: String, success: Boolean) =
if (!success)
println(s"Failed to download $url")
println(s"Failed: $url")
def readingFromCache(f: File) = {}
def puttingInCache(f: File) = {}
@ -62,7 +62,7 @@ case class Coursier(
println(s"Downloading $url")
def downloadedArtifact(url: String, success: Boolean) =
if (!success)
println(s"Failed to download $url")
println(s"Failed: $url")
}
def verboseLogger: MavenRepository.Logger with Files.Logger =
@ -72,7 +72,7 @@ case class Coursier(
def downloaded(url: String, success: Boolean) =
println(
if (success) s"Downloaded $url"
else s"Failed to download $url"
else s"Failed: $url"
)
def readingFromCache(f: File) = {
println(s"Reading ${fileRepr(f)} from cache")
@ -87,7 +87,7 @@ case class Coursier(
def downloadedArtifact(url: String, success: Boolean) =
println(
if (success) s"Downloaded $url"
else s"Failed to download $url"
else s"Failed: $url"
)
}
@ -108,6 +108,7 @@ case class Coursier(
CachePolicy.Default
val cache = Cache.default
cache.init(verbose = verbose0 >= 0)
val repositoryIds = {
val repository0 = repository
@ -121,13 +122,11 @@ case class Coursier(
repository0
}
val existingRepo = cache
.list()
.map(_._1)
.toSet
if (repositoryIds.exists(!existingRepo(_))) {
val repoMap = cache.map()
if (repositoryIds.exists(!repoMap.contains(_))) {
val notFound = repositoryIds
.filter(!existingRepo(_))
.filter(!repoMap.contains(_))
Console.err.println(
(if (notFound.lengthCompare(1) == 1) "Repository" else "Repositories") +
@ -138,10 +137,8 @@ case class Coursier(
sys.exit(1)
}
val (repositories0, fileCaches) = cache
.list()
.map{case (_, repo, cacheEntry) => (repo, cacheEntry)}
val (repositories0, fileCaches) = repositoryIds
.map(repoMap)
.unzip
val repositories = repositories0
@ -223,9 +220,9 @@ case class Coursier(
val errors = res.errors
if (errors.nonEmpty) {
println(s"${errors.size} error(s):")
println(s"\n${errors.size} error(s):")
for ((dep, errs) <- errors) {
println(s" ${dep.module}:\n ${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}")
println(s" ${dep.module}:${dep.version}:\n${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}")
}
}

View File

@ -40,7 +40,7 @@ case class Repositories(
}
if (!cache.cache.exists())
cache.init()
cache.init(verbose = true)
val current = cache.list().map(_._1).toSet

View File

@ -15,7 +15,7 @@ case class MavenRepository(
logger: Option[MavenRepository.Logger] = None
) extends BaseMavenRepository(root, ivyLike) {
val isLocal = root.startsWith("file:///")
val isLocal = root.startsWith("file:/")
def fetch(
artifact: Artifact,
@ -49,9 +49,20 @@ case class MavenRepository(
val url = new URL(urlStr)
def log = Task(logger.foreach(_.downloading(urlStr)))
def get = MavenRepository.readFully(url.openStream())
def get = {
val conn = url.openConnection()
// Dummy user-agent instead of the default "Java/...",
// so that we are not returned incomplete/erroneous metadata
// (Maven 2 compatibility? - happens for snapshot versioning metadata,
// this is SO FUCKING CRAZY)
conn.setRequestProperty("User-Agent", "")
MavenRepository.readFully(conn.getInputStream())
}
def logEnd(success: Boolean) = logger.foreach(_.downloaded(urlStr, success))
log.flatMap(_ => get)
log
.flatMap(_ => get)
.map{ res => logEnd(res.isRight); res }
}
def save(s: String) = {
@ -107,7 +118,12 @@ object MavenRepository {
finally is0.close()
new String(b, "UTF-8")
} .leftMap(_.getMessage)
} .leftMap{
case e: java.io.FileNotFoundException =>
s"Not found: ${e.getMessage}"
case e =>
s"$e: ${e.getMessage}"
}
}
}

View File

@ -56,7 +56,8 @@ case class Project(
dependencyManagement: Seq[Dependency],
properties: Map[String, String],
profiles: Seq[Profile],
versions: Option[Versions]
versions: Option[Versions],
snapshotVersioning: Option[SnapshotVersioning]
) {
def moduleVersion = (module, version)
}
@ -99,6 +100,25 @@ object Versions {
)
}
case class SnapshotVersion(
classifier: String,
extension: String,
value: String,
updated: Option[Versions.DateTime]
)
case class SnapshotVersioning(
module: Module,
version: String,
latest: String,
release: String,
timestamp: String,
buildNumber: Option[Int],
localCopy: Option[Boolean],
lastUpdated: Option[Versions.DateTime],
snapshotVersions: Seq[SnapshotVersion]
)
case class Artifact(
url: String,
checksumUrls: Map[String, String],

View File

@ -3,6 +3,8 @@ package coursier.core
import scalaz.{ -\/, \/-, \/, EitherT }
import scalaz.concurrent.Task
import java.io.File
import coursier.core.compatibility.encodeURIComponent
trait Repository {
@ -21,7 +23,7 @@ object Repository {
val sonatypeReleases = MavenRepository("https://oss.sonatype.org/content/repositories/releases/")
val sonatypeSnapshots = MavenRepository("https://oss.sonatype.org/content/repositories/snapshots/")
lazy val ivy2Local = MavenRepository("file://" + sys.props("user.home") + "/.ivy2/local/", ivyLike = true)
lazy val ivy2Local = MavenRepository(new File(sys.props("user.home") + "/.ivy2/local/").toURI.toString, ivyLike = true)
/**
@ -101,6 +103,7 @@ object Repository {
case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
import Repository._
import BaseMavenRepository._
def artifacts(
dependency: Dependency,
@ -108,7 +111,7 @@ case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
): Seq[Artifact] = {
def ivyLikePath0(subDir: String, baseSuffix: String, ext: String) =
BaseMavenRepository.ivyLikePath(
ivyLikePath(
dependency.module.organization,
dependency.module.name,
project.version,
@ -120,12 +123,20 @@ case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
val path =
if (ivyLike)
ivyLikePath0(dependency.attributes.`type` + "s", "", dependency.attributes.`type`)
else
else {
val versioning =
project
.snapshotVersioning
.flatMap(versioning =>
mavenVersioning(versioning, dependency.attributes.classifier, dependency.attributes.`type`)
)
dependency.module.organization.split('.').toSeq ++ Seq(
dependency.module.name,
project.version,
s"${dependency.module.name}-${project.version}${Some(dependency.attributes.classifier).filter(_.nonEmpty).map("-"+_).mkString}.${dependency.attributes.`type`}"
s"${dependency.module.name}-${versioning getOrElse project.version}${Some(dependency.attributes.classifier).filter(_.nonEmpty).map("-"+_).mkString}.${dependency.attributes.`type`}"
)
}
var artifact =
Artifact(
@ -139,6 +150,9 @@ case class MavenSource(root: String, ivyLike: Boolean) extends Artifact.Source {
if (dependency.attributes.`type` == "jar") {
artifact = artifact.withDefaultSignature
// FIXME Snapshot versioning of sources and javadoc is not taken into account here.
// Will be ok if it's the same as the main JAR though.
artifact =
if (ivyLike) {
val srcPath = root + ivyLikePath0("srcs", "-sources", "jar").mkString("/")
@ -181,6 +195,17 @@ object BaseMavenRepository {
s"$name$baseSuffix.$ext"
)
def mavenVersioning(
snapshotVersioning: SnapshotVersioning,
classifier: String,
extension: String
): Option[String] =
snapshotVersioning
.snapshotVersions
.find(v => v.classifier == classifier && v.extension == extension)
.map(_.value)
.filter(_.nonEmpty)
}
abstract class BaseMavenRepository(
@ -198,16 +223,27 @@ abstract class BaseMavenRepository(
val source = MavenSource(root, ivyLike)
def projectArtifact(module: Module, version: String): Artifact = {
def projectArtifact(
module: Module,
version: String,
versioningValue: Option[String]
): Artifact = {
val path = (
if (ivyLike)
ivyLikePath(module.organization, module.name, version, "poms", "", "pom")
ivyLikePath(
module.organization,
module.name,
versioningValue getOrElse version,
"poms",
"",
"pom"
)
else
module.organization.split('.').toSeq ++ Seq(
module.name,
version,
s"${module.name}-$version.pom"
s"${module.name}-${versioningValue getOrElse version}.pom"
)
) .map(encodeURIComponent)
@ -242,6 +278,32 @@ abstract class BaseMavenRepository(
Some(artifact)
}
def snapshotVersioningArtifact(
module: Module,
version: String
): Option[Artifact] =
if (ivyLike) None
else {
val path = (
module.organization.split('.').toSeq ++ Seq(
module.name,
version,
"maven-metadata.xml"
)
) .map(encodeURIComponent)
val artifact =
Artifact(
path.mkString("/"),
Map.empty,
Map.empty,
Attributes("pom", "")
)
.withDefaultChecksums
Some(artifact)
}
def versions(
module: Module,
cachePolicy: CachePolicy = CachePolicy.Default
@ -265,14 +327,80 @@ abstract class BaseMavenRepository(
)
}
def snapshotVersioning(
module: Module,
version: String,
cachePolicy: CachePolicy = CachePolicy.Default
): EitherT[Task, String, SnapshotVersioning] = {
EitherT(
snapshotVersioningArtifact(module, version) match {
case None => Task.now(-\/("Not supported"))
case Some(artifact) =>
fetch(artifact, cachePolicy)
.run
.map(eitherStr =>
for {
str <- eitherStr
xml <- \/.fromEither(compatibility.xmlParse(str))
_ <- if (xml.label == "metadata") \/-(()) else -\/("Metadata not found")
snapshotVersioning <- Xml.snapshotVersioning(xml)
} yield snapshotVersioning
)
}
)
}
def findNoInterval(
module: Module,
version: String,
cachePolicy: CachePolicy
): EitherT[Task, String, Project] =
EitherT{
def withSnapshotVersioning =
snapshotVersioning(module, version, cachePolicy)
.flatMap { snapshotVersioning =>
val versioningOption =
mavenVersioning(snapshotVersioning, "", "jar")
.orElse(mavenVersioning(snapshotVersioning, "", ""))
versioningOption match {
case None =>
EitherT[Task, String, Project](
Task.now(-\/("No snapshot versioning value found"))
)
case versioning @ Some(_) =>
findVersioning(module, version, versioning, cachePolicy)
.map(_.copy(snapshotVersioning = Some(snapshotVersioning)))
}
}
findVersioning(module, version, None, cachePolicy)
.run
.flatMap{ eitherProj =>
if (eitherProj.isLeft)
withSnapshotVersioning
.run
.map(eitherProj0 =>
if (eitherProj0.isLeft)
eitherProj
else
eitherProj0
)
else
Task.now(eitherProj)
}
}
def findVersioning(
module: Module,
version: String,
versioningValue: Option[String],
cachePolicy: CachePolicy
): EitherT[Task, String, Project] = {
EitherT {
fetch(projectArtifact(module, version), cachePolicy)
fetch(projectArtifact(module, version, versioningValue), cachePolicy)
.run
.map(eitherStr =>
for {

View File

@ -210,10 +210,24 @@ object Xml {
depMgmts,
properties.toMap,
profiles,
None,
None
)
}
def parseDateTime(s: String): Option[Versions.DateTime] =
if (s.length == 14 && s.forall(_.isDigit))
Some(Versions.DateTime(
s.substring(0, 4).toInt,
s.substring(4, 6).toInt,
s.substring(6, 8).toInt,
s.substring(8, 10).toInt,
s.substring(10, 12).toInt,
s.substring(12, 14).toInt
))
else
None
def versions(node: Node): String \/ Versions = {
import Scalaz._
@ -230,24 +244,111 @@ object Xml {
release = text(xmlVersioning, "release", "Release version")
.getOrElse("")
versions <- xmlVersioning.child
versionsOpt = xmlVersioning.child
.find(_.label == "versions")
.map(_.child.filter(_.label == "version").flatMap(_.child.collectFirst{case Text(t) => t}))
.toRightDisjunction("Version list not found in metadata")
lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time")
.toOption
.filter(s => s.length == 14 && s.forall(_.isDigit))
.map(s => Versions.DateTime(
s.substring(0, 4).toInt,
s.substring(4, 6).toInt,
s.substring(6, 8).toInt,
s.substring(8, 10).toInt,
s.substring(10, 12).toInt,
s.substring(12, 14).toInt
))
.flatMap(parseDateTime)
} yield Versions(latest, release, versions.toList, lastUpdatedOpt)
} yield Versions(latest, release, versionsOpt.map(_.toList).getOrElse(Nil), lastUpdatedOpt)
}
def snapshotVersion(node: Node): String \/ SnapshotVersion = {
def textOrEmpty(name: String, desc: String) =
text(node, name, desc)
.toOption
.getOrElse("")
val classifier = textOrEmpty("classifier", "Classifier")
val ext = textOrEmpty("extension", "Extensions")
val value = textOrEmpty("value", "Value")
val updatedOpt = text(node, "updated", "Updated")
.toOption
.flatMap(parseDateTime)
\/-(SnapshotVersion(
classifier,
ext,
value,
updatedOpt
))
}
def snapshotVersioning(node: Node): String \/ SnapshotVersioning = {
import Scalaz._
// FIXME Quite similar to Versions above
for {
organization <- text(node, "groupId", "Organization")
name <- text(node, "artifactId", "Name")
version = readVersion(node)
xmlVersioning <- node.child
.find(_.label == "versioning")
.toRightDisjunction("Versioning info not found in metadata")
latest = text(xmlVersioning, "latest", "Latest version")
.getOrElse("")
release = text(xmlVersioning, "release", "Release version")
.getOrElse("")
versionsOpt = xmlVersioning.child
.find(_.label == "versions")
.map(_.child.filter(_.label == "version").flatMap(_.child.collectFirst{case Text(t) => t}))
lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time")
.toOption
.flatMap(parseDateTime)
xmlSnapshotOpt = xmlVersioning.child
.find(_.label == "snapshot")
timestamp = xmlSnapshotOpt
.flatMap(
text(_, "timestamp", "Snapshot timestamp")
.toOption
)
.getOrElse("")
buildNumber = xmlSnapshotOpt
.flatMap(
text(_, "buildNumber", "Snapshot build number")
.toOption
)
.filter(s => s.nonEmpty && s.forall(_.isDigit))
.map(_.toInt)
localCopy = xmlSnapshotOpt
.flatMap(
text(_, "localCopy", "Snapshot local copy")
.toOption
)
.collect{
case "true" => true
case "false" => false
}
xmlSnapshotVersions = xmlVersioning.child
.find(_.label == "snapshotVersions")
.map(_.child.filter(_.label == "snapshotVersion"))
.getOrElse(Seq.empty)
snapshotVersions <- xmlSnapshotVersions
.toList
.traverseU(snapshotVersion)
} yield SnapshotVersioning(
Module(organization, name),
version,
latest,
release,
timestamp,
buildNumber,
localCopy,
lastUpdatedOpt,
snapshotVersions
)
}
}

View File

@ -30,7 +30,8 @@ package object test {
dependencyManagement: Seq[Dependency] = Seq.empty,
properties: Map[String, String] = Map.empty,
profiles: Seq[Profile] = Seq.empty,
versions: Option[core.Versions] = None): Project =
core.Project(module, version, dependencies, parent, dependencyManagement, properties, profiles, versions)
versions: Option[core.Versions] = None,
snapshotVersioning: Option[core.SnapshotVersioning] = None): Project =
core.Project(module, version, dependencies, parent, dependencyManagement, properties, profiles, versions, snapshotVersioning)
}
}

View File

@ -61,10 +61,15 @@ case class Cache(cache: File) {
add("central", "https://repo1.maven.org/maven2/", ivyLike = false)
def addIvy2Local(): Unit =
add("ivy2local", "file://" + sys.props("user.home") + "/.ivy2/local/", ivyLike = true)
add("ivy2local", new File(sys.props("user.home") + "/.ivy2/local/").toURI.toString, ivyLike = true)
def init(ifEmpty: Boolean = true): Unit =
def init(
ifEmpty: Boolean = true,
verbose: Boolean = false
): Unit =
if (!ifEmpty || !cache.exists()) {
if (verbose)
Console.err.println(s"Initializing $cache")
repoDir.mkdirs()
metadataBase.mkdirs()
fileBase.mkdirs()
@ -129,11 +134,7 @@ case class Cache(cache: File) {
} else
Nil
def files(): Files = {
val map0 = map()
val default0 = default()
new Files(default0.map(map0(_)._2), () => ???)
}
def files(): Files =
new Files(list().map{case (_, _, matching) => matching }, () => ???)
}

View File

@ -23,12 +23,14 @@ case class Files(
def withLocal(artifact: Artifact): Artifact = {
val isLocal =
artifact.url.startsWith("file://") &&
artifact.checksumUrls.values.forall(_.startsWith("file://"))
artifact.url.startsWith("file:/") &&
artifact.checksumUrls.values.forall(_.startsWith("file:/"))
def local(url: String) =
if (url.startsWith("file://"))
if (url.startsWith("file:///"))
url.stripPrefix("file://")
else if (url.startsWith("file:/"))
url.stripPrefix("file:")
else
cache.find{case (base, _) => url.startsWith(base)} match {
case None => ???
@ -139,7 +141,7 @@ case class Files(
val tasks =
for ((f, url) <- pairs if url != ("file://" + f)) yield {
for ((f, url) <- pairs if url != ("file:" + f) && url != ("file://" + f)) yield {
val file = new File(f)
cachePolicy(locally(file))(remote(file, url))
.map(e => (file, url) -> e.map(_ => ()))