diff --git a/cli/src/main/scala/coursier/cli/Coursier.scala b/cli/src/main/scala/coursier/cli/Coursier.scala index 3f06b9891..b1cbef11b 100644 --- a/cli/src/main/scala/coursier/cli/Coursier.scala +++ b/cli/src/main/scala/coursier/cli/Coursier.scala @@ -22,9 +22,7 @@ case class Coursier(scope: List[String], val centralCacheDir = new File(sys.props("user.home") + "/.coursier/cache/metadata/central") val centralFilesCacheDir = new File(sys.props("user.home") + "/.coursier/cache/files/central") - val base = centralCacheDir.toURI - def fileRepr(f: File) = - base.relativize(f.toURI).getPath + def fileRepr(f: File) = f.toString val logger: MetadataFetchLogger with FilesLogger = new MetadataFetchLogger with FilesLogger { def println(s: String) = Console.err.println(s) @@ -60,7 +58,12 @@ case class Coursier(scope: List[String], ) ) val repositories = Seq[Repository]( - cachedMavenCentral + cachedMavenCentral, + repository.ivy2Local.copy( + fetchMetadata = repository.ivy2Local.fetchMetadata.copy( + logger = Some(logger) + ) + ) ) val (splitDependencies, malformed) = remainingArgs.toList @@ -132,7 +135,13 @@ case class Coursier(scope: List[String], val artifacts = res.artifacts - val files = new Files(Seq(cachedMavenCentral.fetchMetadata.root -> centralFilesCacheDir), () => ???, Some(logger)) + val files = new Files( + Seq( + cachedMavenCentral.fetchMetadata.root -> centralFilesCacheDir + ), + () => ???, + Some(logger) + ) val tasks = artifacts.map(files.file(_, cachePolicy).run) val task = Task.gatherUnordered(tasks) diff --git a/core-jvm/src/main/scala/coursier/core/DefaultFetchMetadata.scala b/core-jvm/src/main/scala/coursier/core/DefaultFetchMetadata.scala index 30f7f87af..70bd9dea9 100644 --- a/core-jvm/src/main/scala/coursier/core/DefaultFetchMetadata.scala +++ b/core-jvm/src/main/scala/coursier/core/DefaultFetchMetadata.scala @@ -2,7 +2,7 @@ package coursier package core import java.io._ -import java.net.URL +import java.net.{URI, URL} import scala.io.Codec import scalaz._, Scalaz._ @@ -19,18 +19,13 @@ case class DefaultFetchMetadata(root: String, cache: Option[File] = None, logger: Option[MetadataFetchLogger] = None) extends FetchMetadata { - def apply(artifact: Artifact, cachePolicy: CachePolicy): EitherT[Task, String, String] = { - lazy val localFile = { - for { - cache0 <- cache.toRightDisjunction("No cache") - f = new File(cache0, artifact.url) - } yield f - } + val isLocal = root.startsWith("file:///") - def locally = { + def apply(artifact: Artifact, cachePolicy: CachePolicy): EitherT[Task, String, String] = { + def locally(eitherFile: String \/ File) = { Task { for { - f0 <- localFile + f0 <- eitherFile f <- Some(f0).filter(_.exists()).toRightDisjunction("Not found in cache") content <- \/.fromTryCatchNonFatal{ logger.foreach(_.readingFromCache(f)) @@ -40,32 +35,42 @@ case class DefaultFetchMetadata(root: String, } } - def remote = { - val urlStr = root + artifact.url - val url = new URL(urlStr) + if (isLocal) EitherT(locally(\/-(new File(new URI(root + artifact.url) .getPath)))) + else { + lazy val localFile = { + for { + cache0 <- cache.toRightDisjunction("No cache") + f = new File(cache0, artifact.url) + } yield f + } - def log = Task(logger.foreach(_.downloading(urlStr))) - def get = DefaultFetchMetadata.readFully(url.openStream()) + def remote = { + val urlStr = root + artifact.url + val url = new URL(urlStr) - log.flatMap(_ => get) - } + def log = Task(logger.foreach(_.downloading(urlStr))) + def get = DefaultFetchMetadata.readFully(url.openStream()) - def save(s: String) = { - localFile.fold(_ => Task.now(()), f => - Task { - if (!f.exists()) { - logger.foreach(_.puttingInCache(f)) - f.getParentFile.mkdirs() - val w = new PrintWriter(f) - try w.write(s) - finally w.close() - () + log.flatMap(_ => get) + } + + def save(s: String) = { + localFile.fold(_ => Task.now(()), f => + Task { + if (!f.exists()) { + logger.foreach(_.puttingInCache(f)) + f.getParentFile.mkdirs() + val w = new PrintWriter(f) + try w.write(s) + finally w.close() + () + } } - } - ) - } + ) + } - EitherT(cachePolicy.saving(locally)(remote)(save)) + EitherT(cachePolicy.saving(locally(localFile))(remote)(save)) + } } } diff --git a/core/src/main/scala/coursier/core/Repository.scala b/core/src/main/scala/coursier/core/Repository.scala index d95d32eef..a8bb44f7d 100644 --- a/core/src/main/scala/coursier/core/Repository.scala +++ b/core/src/main/scala/coursier/core/Repository.scala @@ -66,7 +66,7 @@ object Repository { Artifact.javadoc -> (base + "-javadoc.jar"), Artifact.javadocSigMd5 -> (base + "-javadoc.jar.asc.md5"), Artifact.javadocSigSha1 -> (base + "-javadoc.jar.asc.sha1"), - Artifact.javadocSig -> (base + "-javadoc.asc.jar") + Artifact.javadocSig -> (base + "-javadoc.jar.asc") )) } } @@ -83,27 +83,35 @@ case class MavenRepository[F <: FetchMetadata](fetchMetadata: F, import Repository._ + def ivyLikePath(org: String, name: String, version: String, subDir: String, baseSuffix: String, ext: String) = Seq( + org, + name, + version, + subDir, + s"$name$baseSuffix.$ext" + ) + def projectArtifact(module: Module, version: String): Artifact = { - if (ivyLike) ??? - else { - val path = ( + val path = ( + if (ivyLike) + ivyLikePath(module.organization, module.name, version, "poms", "", "pom") + else module.organization.split('.').toSeq ++ Seq( module.name, version, s"${module.name}-$version.pom" ) - ) .map(encodeURIComponent) + ) .map(encodeURIComponent) - Artifact( - path.mkString("/"), - Map( - Artifact.md5 -> "", - Artifact.sha1 -> "" - ), - Attributes("pom", "") - ) - .withDefaultSignature - } + Artifact( + path.mkString("/"), + Map( + Artifact.md5 -> "", + Artifact.sha1 -> "" + ), + Attributes("pom", "") + ) + .withDefaultSignature } def versionsArtifact(module: Module): Option[Artifact] = @@ -202,12 +210,18 @@ case class MavenRepository[F <: FetchMetadata](fetchMetadata: F, def artifacts(dependency: Dependency, project: Project): Seq[Artifact] = { + def ivyLikePath0(subDir: String, baseSuffix: String, ext: String) = + ivyLikePath(dependency.module.organization, dependency.module.name, project.version, subDir, baseSuffix, ext) + val path = - 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`}" - ) + if (ivyLike) + ivyLikePath0(dependency.attributes.`type` + "s", "", dependency.attributes.`type`) + else + 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`}" + ) var artifact = Artifact( @@ -217,10 +231,31 @@ case class MavenRepository[F <: FetchMetadata](fetchMetadata: F, ) .withDefaultChecksums - if (dependency.attributes.`type` == "jar") - artifact = artifact - .withDefaultSignature - .withJavadocSources + if (dependency.attributes.`type` == "jar") { + artifact = artifact.withDefaultSignature + + artifact = + if (ivyLike) { + val srcPath = fetchMetadata.root + ivyLikePath0("srcs", "-sources", "jar").mkString("/") + val javadocPath = fetchMetadata.root + ivyLikePath0("docs", "-javadoc", "jar").mkString("/") + + artifact + .copy(extra = artifact.extra ++ Map( + Artifact.sourcesMd5 -> (srcPath + ".md5"), + Artifact.sourcesSha1 -> (srcPath + ".sha1"), + Artifact.sources -> srcPath, + Artifact.sourcesSigMd5 -> (srcPath + ".asc.md5"), + Artifact.sourcesSigSha1 -> (srcPath + ".asc.sha1"), + Artifact.sourcesSig -> (srcPath + ".asc"), + Artifact.javadocMd5 -> (javadocPath + ".md5"), + Artifact.javadocSha1 -> (javadocPath + ".sha1"), + Artifact.javadoc -> javadocPath, + Artifact.javadocSigMd5 -> (javadocPath + ".asc.md5"), + Artifact.javadocSigSha1 -> (javadocPath + ".asc.sha1"), + Artifact.javadocSig -> (javadocPath + ".asc") + )) + } else artifact.withJavadocSources + } Seq(artifact) } diff --git a/core/src/main/scala/coursier/repository/package.scala b/core/src/main/scala/coursier/repository/package.scala index c448f25c5..93acd2c09 100644 --- a/core/src/main/scala/coursier/repository/package.scala +++ b/core/src/main/scala/coursier/repository/package.scala @@ -9,4 +9,6 @@ package object repository { val sonatypeReleases = MavenRepository(DefaultFetchMetadata("https://oss.sonatype.org/content/repositories/releases/")) val sonatypeSnapshots = MavenRepository(DefaultFetchMetadata("https://oss.sonatype.org/content/repositories/snapshots/")) + lazy val ivy2Local = MavenRepository(DefaultFetchMetadata("file://" + sys.props("user.home") + "/.ivy2/local/"), ivyLike = true) + } diff --git a/files/src/main/scala/coursier/Files.scala b/files/src/main/scala/coursier/Files.scala index 014c56ea9..9d847c27e 100644 --- a/files/src/main/scala/coursier/Files.scala +++ b/files/src/main/scala/coursier/Files.scala @@ -1,6 +1,6 @@ package coursier -import java.net.URL +import java.net.{URI, URL} import coursier.core.CachePolicy @@ -24,65 +24,75 @@ case class Files(cache: Seq[(String, File)], def file(artifact: Artifact, cachePolicy: CachePolicy): EitherT[Task, String, File] = { - cache.find{case (base, _) => artifact.url.startsWith(base)} match { - case None => ??? - case Some((base, cacheDir)) => - val file = new File(cacheDir, artifact.url.stripPrefix(base)) + if (artifact.url.startsWith("file:///")) { + val f = new File(new URI(artifact.url) .getPath) + EitherT(Task.now( + if (f.exists()) { + logger.foreach(_.foundLocally(f)) + \/-(f) + } else -\/("Not found") + )) + } else { + cache.find{case (base, _) => artifact.url.startsWith(base)} match { + case None => ??? + case Some((base, cacheDir)) => + val file = new File(cacheDir, artifact.url.stripPrefix(base)) - def locally = { - Task { - if (file.exists()) { - logger.foreach(_.foundLocally(file)) - \/-(file) + def locally = { + Task { + if (file.exists()) { + logger.foreach(_.foundLocally(file)) + \/-(file) + } + else -\/("Not found in cache") } - else -\/("Not found in cache") } - } - def remote = { - // FIXME A lot of things can go wrong here and are not properly handled: - // - checksums should be validated - // - what if the connection gets closed during the transfer (partial file on disk)? - // - what if someone is trying to write this file at the same time? (no locking of any kind yet) - // - ... - - Task { - try { - file.getParentFile.mkdirs() - - logger.foreach(_.downloadingArtifact(artifact.url)) - - val url = new URL(artifact.url) - val b = Array.fill[Byte](Files.bufferSize)(0) - val in = new BufferedInputStream(url.openStream(), Files.bufferSize) + def remote = { + // FIXME A lot of things can go wrong here and are not properly handled: + // - checksums should be validated + // - what if the connection gets closed during the transfer (partial file on disk)? + // - what if someone is trying to write this file at the same time? (no locking of any kind yet) + // - ... + Task { try { - val out = new FileOutputStream(file) + file.getParentFile.mkdirs() + + logger.foreach(_.downloadingArtifact(artifact.url)) + + val url = new URL(artifact.url) + val b = Array.fill[Byte](Files.bufferSize)(0) + val in = new BufferedInputStream(url.openStream(), Files.bufferSize) + try { - @tailrec - def helper(): Unit = { - val read = in.read(b) - if (read >= 0) { - out.write(b, 0, read) - helper() + val out = new FileOutputStream(file) + try { + @tailrec + def helper(): Unit = { + val read = in.read(b) + if (read >= 0) { + out.write(b, 0, read) + helper() + } } - } - helper() - } finally out.close() - } finally in.close() + helper() + } finally out.close() + } finally in.close() - logger.foreach(_.downloadedArtifact(artifact.url, success = true)) - \/-(file) - } - catch { case e: Exception => - logger.foreach(_.downloadedArtifact(artifact.url, success = false)) - -\/(e.getMessage) + logger.foreach(_.downloadedArtifact(artifact.url, success = true)) + \/-(file) + } + catch { case e: Exception => + logger.foreach(_.downloadedArtifact(artifact.url, success = false)) + -\/(e.getMessage) + } } } - } - EitherT(cachePolicy(locally)(remote)) + EitherT(cachePolicy(locally)(remote)) + } } }