sbt/plugin/src/main/scala-2.10/coursier/Tasks.scala

1044 lines
32 KiB
Scala
Raw Normal View History

2015-12-30 01:34:34 +01:00
package coursier
2015-12-30 01:34:43 +01:00
import java.io.{ OutputStreamWriter, File }
import java.net.URL
import java.nio.file.Files
import java.util.concurrent.{ ExecutorService, Executors }
2015-12-30 01:34:39 +01:00
import coursier.core.{ Authentication, Publication }
import coursier.ivy.{ IvyRepository, PropertiesPattern }
2015-12-30 01:34:43 +01:00
import coursier.Keys._
import coursier.Structure._
import coursier.maven.WritePom
2016-01-10 21:32:28 +01:00
import coursier.util.{ Config, Print }
import org.apache.ivy.core.module.id.ModuleRevisionId
2015-12-30 01:34:43 +01:00
import sbt.{ UpdateReport, Classpaths, Resolver, Def }
import sbt.Configurations.{ Compile, Test }
2015-12-30 01:34:34 +01:00
import sbt.Keys._
2015-12-30 01:34:43 +01:00
import scala.collection.mutable
import scala.collection.JavaConverters._
2016-08-15 16:49:20 +02:00
import scala.collection.mutable.ArrayBuffer
import scala.util.Try
2015-12-30 01:34:43 +01:00
import scalaz.{ \/-, -\/ }
2015-12-30 01:34:41 +01:00
import scalaz.concurrent.{ Task, Strategy }
2015-12-30 01:34:39 +01:00
2015-12-30 01:34:34 +01:00
object Tasks {
def coursierResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] =
(
externalResolvers,
sbtPlugin,
sbtResolver,
bootResolvers,
overrideBuildResolvers
).map { (extRes, isSbtPlugin, sbtRes, bootResOpt, overrideFlag) =>
bootResOpt.filter(_ => overrideFlag).getOrElse {
var resolvers = extRes
if (isSbtPlugin)
resolvers = Seq(
sbtRes,
Classpaths.sbtPluginReleases
) ++ resolvers
resolvers
}
}
2015-12-30 01:34:34 +01:00
def coursierFallbackDependenciesTask: Def.Initialize[sbt.Task[Seq[(Module, String, URL, Boolean)]]] =
(
sbt.Keys.state,
sbt.Keys.thisProjectRef
).flatMap { (state, projectRef) =>
val allDependenciesTask = allDependencies.in(projectRef).get(state)
for {
allDependencies <- allDependenciesTask
} yield {
FromSbt.fallbackDependencies(
allDependencies,
scalaVersion.in(projectRef).get(state),
scalaBinaryVersion.in(projectRef).get(state)
)
}
}
def coursierProjectTask: Def.Initialize[sbt.Task[Project]] =
2015-12-30 01:34:34 +01:00
(
sbt.Keys.state,
sbt.Keys.thisProjectRef
).flatMap { (state, projectRef) =>
// should projectID.configurations be used instead?
val configurations = ivyConfigurations.in(projectRef).get(state)
val allDependenciesTask = allDependencies.in(projectRef).get(state)
lazy val projId = projectID.in(projectRef).get(state)
lazy val sv = scalaVersion.in(projectRef).get(state)
lazy val sbv = scalaBinaryVersion.in(projectRef).get(state)
lazy val defaultArtifactType = coursierDefaultArtifactType.in(projectRef).get(state)
2015-12-30 01:34:34 +01:00
for {
allDependencies <- allDependenciesTask
} yield {
FromSbt.project(
projId,
2015-12-30 01:34:34 +01:00
allDependencies,
configurations.map { cfg => cfg.name -> cfg.extendsConfigs.map(_.name) }.toMap,
sv,
sbv,
defaultArtifactType
2015-12-30 01:34:34 +01:00
)
}
}
def coursierInterProjectDependenciesTask: Def.Initialize[sbt.Task[Seq[Project]]] =
(
sbt.Keys.state,
sbt.Keys.thisProjectRef
).flatMap { (state, projectRef) =>
def dependencies(map: Map[String, Seq[String]], id: String): Set[String] = {
def helper(map: Map[String, Seq[String]], acc: Set[String]): Set[String] =
if (acc.exists(map.contains)) {
val (kept, rem) = map.partition { case (k, _) => acc(k) }
helper(rem, acc ++ kept.valuesIterator.flatten)
} else
acc
helper(map - id, map.getOrElse(id, Nil).toSet)
}
val allProjectsDeps =
for (p <- structure(state).allProjects)
yield p.id -> p.dependencies.map(_.project.project)
val deps = dependencies(allProjectsDeps.toMap, projectRef.project)
val projects = structure(state).allProjectRefs.filter(p => deps(p.project))
2015-12-30 01:34:34 +01:00
coursierProject.forAllProjects(state, projects).map(_.values.toVector)
}
def coursierPublicationsTask: Def.Initialize[sbt.Task[Seq[(String, Publication)]]] =
(
sbt.Keys.state,
sbt.Keys.thisProjectRef,
sbt.Keys.projectID,
sbt.Keys.scalaVersion,
2016-02-20 15:53:09 +01:00
sbt.Keys.scalaBinaryVersion,
sbt.Keys.ivyConfigurations
).map { (state, projectRef, projId, sv, sbv, ivyConfs) =>
val packageTasks = Seq(packageBin, packageSrc, packageDoc)
val configs = Seq(Compile, Test)
val sbtArtifacts =
for {
pkgTask <- packageTasks
config <- configs
} yield {
val publish = publishArtifact.in(projectRef).in(pkgTask).in(config).getOrElse(state, false)
if (publish)
Option(artifact.in(projectRef).in(pkgTask).in(config).getOrElse(state, null))
.map(config.name -> _)
else
None
}
2016-02-20 15:53:09 +01:00
def artifactPublication(artifact: sbt.Artifact) = {
val name = FromSbt.sbtCrossVersionName(
artifact.name,
projId.crossVersion,
sv,
sbv
)
Publication(
name,
artifact.`type`,
artifact.extension,
artifact.classifier.getOrElse("")
)
}
val sbtArtifactsPublication = sbtArtifacts.collect {
case Some((config, artifact)) =>
2016-02-20 15:53:09 +01:00
config -> artifactPublication(artifact)
}
2016-02-20 15:53:09 +01:00
val stdArtifactsSet = sbtArtifacts.flatMap(_.map { case (_, a) => a }.toSeq).toSet
// Second-way of getting artifacts from SBT
// No obvious way of getting the corresponding publishArtifact value for the ones
// only here, it seems.
val extraSbtArtifacts = Option(artifacts.in(projectRef).getOrElse(state, null))
.toSeq
.flatten
.filterNot(stdArtifactsSet)
// Seems that SBT does that - if an artifact has no configs,
// it puts it in all of them. See for example what happens to
// the standalone JAR artifact of the coursier cli module.
def allConfigsIfEmpty(configs: Iterable[sbt.Configuration]): Iterable[sbt.Configuration] =
if (configs.isEmpty) ivyConfs else configs
val extraSbtArtifactsPublication = for {
artifact <- extraSbtArtifacts
config <- allConfigsIfEmpty(artifact.configurations) if config.isPublic
} yield config.name -> artifactPublication(artifact)
sbtArtifactsPublication ++ extraSbtArtifactsPublication
}
2016-05-06 13:53:49 +02:00
def coursierConfigurationsTask = Def.task {
val configs0 = ivyConfigurations.value.map { config =>
config.name -> config.extendsConfigs.map(_.name)
}.toMap
def allExtends(c: String) = {
// possibly bad complexity
def helper(current: Set[String]): Set[String] = {
val newSet = current ++ current.flatMap(configs0.getOrElse(_, Nil))
if ((newSet -- current).nonEmpty)
helper(newSet)
else
newSet
}
helper(Set(c))
}
configs0.map {
case (config, _) =>
config -> allExtends(config)
}
}
private case class ResolutionCacheKey(
project: Project,
repositories: Seq[Repository],
2016-08-04 01:36:53 +02:00
userEnabledProfiles: Set[String],
2015-12-30 01:34:43 +01:00
resolution: Resolution,
2016-05-06 13:53:49 +02:00
sbtClassifiers: Boolean
)
private case class ReportCacheKey(
project: Project,
resolution: Resolution,
2015-12-30 01:34:43 +01:00
withClassifiers: Boolean,
sbtClassifiers: Boolean
)
2016-05-06 13:53:49 +02:00
private val resolutionsCache = new mutable.HashMap[ResolutionCacheKey, Resolution]
// these may actually not need to be cached any more, now that the resolutions
// are cached
private val reportsCache = new mutable.HashMap[ReportCacheKey, UpdateReport]
2015-12-30 01:34:43 +01:00
private def forcedScalaModules(
scalaOrganization: String,
scalaVersion: String
): Map[Module, String] =
Map(
Module(scalaOrganization, "scala-library") -> scalaVersion,
Module(scalaOrganization, "scala-compiler") -> scalaVersion,
Module(scalaOrganization, "scala-reflect") -> scalaVersion,
Module(scalaOrganization, "scalap") -> scalaVersion
)
2016-05-06 13:53:49 +02:00
private def projectDescription(project: Project) =
s"${project.module.organization}:${project.module.name}:${project.version}"
2015-12-30 01:34:39 +01:00
private def createLogger() = new TermDisplay(new OutputStreamWriter(System.err))
private lazy val globalPluginPattern = {
2016-08-15 16:49:20 +02:00
val props = sys.props.toMap
val extraProps = new ArrayBuffer[(String, String)]
def addUriProp(key: String): Unit =
for (b <- props.get(key)) {
val uri = new File(b).toURI.toString
extraProps += s"$key.uri" -> uri
}
addUriProp("sbt.global.base")
addUriProp("user.home")
// FIXME get the 0.13 automatically?
2016-08-15 16:49:20 +02:00
val s = s"$${sbt.global.base.uri-$${user.home.uri}/.sbt/0.13}/plugins/target/resolution-cache/" +
"[organization]/[module](/scala_[scalaVersion])(/sbt_[sbtVersion])/[revision]/resolved.xml.[ext]"
val p = PropertiesPattern.parse(s) match {
case -\/(err) =>
throw new Exception(s"Cannot parse pattern $s: $err")
case \/-(p) =>
p
}
2016-08-15 16:49:20 +02:00
p.substituteProperties(props ++ extraProps) match {
case -\/(err) =>
throw new Exception(err)
case \/-(p) =>
p
}
}
2016-05-06 13:53:49 +02:00
def resolutionTask(
sbtClassifiers: Boolean = false
) = Def.task {
2015-12-30 01:34:39 +01:00
// let's update only one module at once, for a better output
// Downloads are already parallel, no need to parallelize further anyway
synchronized {
lazy val cm = coursierSbtClassifiersModule.value
val (currentProject, fallbackDependencies) =
if (sbtClassifiers) {
val sv = scalaVersion.value
val sbv = scalaBinaryVersion.value
val defaultArtifactType = coursierDefaultArtifactType.value
val proj = FromSbt.project(
2015-12-30 01:34:39 +01:00
cm.id,
cm.modules,
cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap,
sv,
sbv,
defaultArtifactType
2015-12-30 01:34:39 +01:00
)
val fallbackDeps = FromSbt.fallbackDependencies(
cm.modules,
sv,
sbv
)
(proj, fallbackDeps)
} else {
val proj = coursierProject.value
val publications = coursierPublications.value
val fallbackDeps = coursierFallbackDependencies.value
(proj.copy(publications = publications), fallbackDeps)
2015-12-30 01:34:39 +01:00
}
val interProjectDependencies = coursierInterProjectDependencies.value
2015-12-30 01:34:39 +01:00
val parallelDownloads = coursierParallelDownloads.value
val checksums = coursierChecksums.value
val maxIterations = coursierMaxIterations.value
val cachePolicies = coursierCachePolicies.value
2016-05-31 15:18:29 +02:00
val ttl = coursierTtl.value
val cache = coursierCache.value
2015-12-30 01:34:39 +01:00
2016-04-05 16:24:39 +02:00
val log = streams.value.log
// are these always defined? (e.g. for Java only projects?)
val so = scalaOrganization.value
val sv = scalaVersion.value
val sbv = scalaBinaryVersion.value
val userForceVersions = dependencyOverrides.value.map(
FromSbt.moduleVersion(_, sv, sbv)
).toMap
var anyNonSupportedExclusionRule = false
val exclusions = excludeDependencies.value.flatMap {
rule =>
if (
rule.artifact != "*" ||
rule.configurations.nonEmpty
) {
2016-04-05 16:24:39 +02:00
log.warn(s"Unsupported exclusion rule $rule")
anyNonSupportedExclusionRule = true
Nil
} else
Seq((rule.organization,
FromSbt.sbtCrossVersionName(rule.name, rule.crossVersion, sv, sbv)))
}.toSet
if (anyNonSupportedExclusionRule)
2016-04-05 16:24:39 +02:00
log.warn("Only supported exclusion rule fields: organization, name")
val resolvers =
if (sbtClassifiers)
coursierSbtResolvers.value
else
coursierResolvers.value
2015-12-30 01:34:39 +01:00
val sourceRepositories = coursierSourceRepositories.value.map { dir =>
// FIXME Don't hardcode this path?
new File(dir, "target/repository")
}
val sourceRepositoriesForcedDependencies = sourceRepositories.flatMap {
base =>
def pomDirComponents(f: File, components: Vector[String]): Stream[Vector[String]] =
if (f.isDirectory) {
val components0 = components :+ f.getName
Option(f.listFiles()).toStream.flatten.flatMap(pomDirComponents(_, components0))
} else if (f.getName.endsWith(".pom"))
Stream(components)
else
Stream.empty
Option(base.listFiles())
.toVector
.flatten
.flatMap(pomDirComponents(_, Vector()))
// at least 3 for org / name / version - the contrary should not happen, but who knows
.filter(_.length >= 3)
.map { components =>
val org = components.dropRight(2).mkString(".")
val name = components(components.length - 2)
val version = components.last
Module(org, name) -> version
}
}
// TODO Warn about possible duplicated modules from source repositories?
val verbosityLevel = coursierVerbosity.value
2015-12-30 01:34:39 +01:00
val userEnabledProfiles = mavenProfiles.value
2015-12-30 01:34:39 +01:00
val startRes = Resolution(
currentProject.dependencies.map {
case (_, dep) =>
dep.copy(exclusions = dep.exclusions ++ exclusions)
}.toSet,
2015-12-30 01:34:39 +01:00
filter = Some(dep => !dep.optional),
profileActivation = Some(core.Resolution.userProfileActivation(userEnabledProfiles)),
forceVersions =
// order matters here
userForceVersions ++
sourceRepositoriesForcedDependencies ++
forcedScalaModules(so, sv) ++
interProjectDependencies.map(_.moduleVersion)
2015-12-30 01:34:39 +01:00
)
if (verbosityLevel >= 2) {
2016-04-05 16:24:39 +02:00
log.info("InterProjectRepository")
for (p <- interProjectDependencies)
2016-04-05 16:24:39 +02:00
log.info(s" ${p.module}:${p.version}")
}
2015-12-30 01:34:39 +01:00
val globalPluginsRepo = IvyRepository.fromPattern(
globalPluginPattern,
withChecksums = false,
withSignatures = false,
withArtifacts = false
)
2015-12-30 01:34:39 +01:00
val interProjectRepo = InterProjectRepository(interProjectDependencies)
2015-12-30 01:34:43 +01:00
val internalSbtScalaProvider = appConfiguration.value.provider.scalaProvider
val internalSbtScalaJarsRepo = SbtScalaJarsRepository(
so, // this seems plain wrong - this assumes that the scala org of the project is the same
// as the one that started SBT. This will scrap the scala org specific JARs by the ones
// that booted SBT, even if the latter come from the standard org.scala-lang org.
// But SBT itself does it this way, and not doing so may make two different versions
// of the scala JARs land in the classpath...
internalSbtScalaProvider.version(),
internalSbtScalaProvider.jars()
)
val ivyHome = sys.props.getOrElse(
"ivy.home",
new File(sys.props("user.home")).toURI.getPath + ".ivy2"
)
val sbtIvyHome = sys.props.getOrElse(
"sbt.ivy.home",
ivyHome
)
val ivyProperties = Map(
"ivy.home" -> ivyHome,
"sbt.ivy.home" -> sbtIvyHome
) ++ sys.props
2015-12-30 01:34:43 +01:00
val useSbtCredentials = coursierUseSbtCredentials.value
val authenticationByHost =
if (useSbtCredentials) {
val cred = sbt.Keys.credentials.value.map(sbt.Credentials.toDirect)
cred.map { c =>
c.host -> Authentication(c.userName, c.passwd)
}.toMap
} else
Map.empty[String, Authentication]
val authenticationByRepositoryId = coursierCredentials.value.mapValues(_.authentication)
val sourceRepositories0 = sourceRepositories.map {
base =>
MavenRepository(base.toURI.toString, changing = Some(true))
}
val fallbackDependenciesRepositories =
if (fallbackDependencies.isEmpty)
Nil
else {
val map = fallbackDependencies.map {
case (mod, ver, url, changing) =>
(mod, ver) -> ((url, changing))
}.toMap
Seq(
FallbackDependenciesRepository(map)
)
}
def withAuthenticationByHost(repo: Repository, credentials: Map[String, Authentication]): Repository = {
def httpHost(s: String) =
if (s.startsWith("http://") || s.startsWith("https://"))
Try(Cache.url(s).getHost).toOption
else
None
repo match {
case m: MavenRepository =>
if (m.authentication.isEmpty)
httpHost(m.root).flatMap(credentials.get).fold(m) { auth =>
m.copy(authentication = Some(auth))
}
else
m
case i: IvyRepository =>
if (i.authentication.isEmpty) {
2016-07-03 17:21:17 +02:00
val base = i.pattern.chunks.takeWhile {
case _: coursier.ivy.Pattern.Chunk.Const => true
case _ => false
}.map(_.string).mkString
httpHost(base).flatMap(credentials.get).fold(i) { auth =>
i.copy(authentication = Some(auth))
}
} else
i
case _ =>
repo
}
}
val internalRepositories = Seq(globalPluginsRepo, interProjectRepo, internalSbtScalaJarsRepo)
val repositories =
internalRepositories ++
sourceRepositories0 ++
resolvers.flatMap { resolver =>
FromSbt.repository(
resolver,
ivyProperties,
log,
authenticationByRepositoryId.get(resolver.name)
)
}.map(withAuthenticationByHost(_, authenticationByHost)) ++
fallbackDependenciesRepositories
2015-12-30 01:34:39 +01:00
2016-05-06 13:53:49 +02:00
def resolution = {
var pool: ExecutorService = null
var resLogger: TermDisplay = null
try {
pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
resLogger = createLogger()
val fetch = Fetch.from(
repositories,
Cache.fetch(cache, cachePolicies.head, checksums = checksums, logger = Some(resLogger), pool = pool, ttl = ttl),
cachePolicies.tail.map(p =>
Cache.fetch(cache, p, checksums = checksums, logger = Some(resLogger), pool = pool, ttl = ttl)
): _*
)
2015-12-30 01:34:43 +01:00
def depsRepr(deps: Seq[(String, Dependency)]) =
deps.map { case (config, dep) =>
s"${dep.module}:${dep.version}:$config->${dep.configuration}"
}.sorted.distinct
if (verbosityLevel >= 2) {
val repoReprs = repositories.map {
case r: IvyRepository =>
s"ivy:${r.pattern}"
case r: InterProjectRepository =>
"inter-project"
case r: MavenRepository =>
r.root
case r =>
// should not happen
r.toString
}
2015-12-30 01:34:39 +01:00
log.info(
"Repositories:\n" +
repoReprs.map(" " + _).mkString("\n")
)
2015-12-30 01:34:45 +01:00
}
2016-01-10 21:32:28 +01:00
if (verbosityLevel >= 0)
log.info(
s"Updating ${projectDescription(currentProject)}" +
(if (sbtClassifiers) " (sbt classifiers)" else "")
)
if (verbosityLevel >= 2)
for (depRepr <- depsRepr(currentProject.dependencies))
log.info(s" $depRepr")
resLogger.init()
val res = startRes
.process
.run(fetch, maxIterations)
.attemptRun
.leftMap(ex =>
ResolutionError.UnknownException(ex)
.throwException()
)
.merge
2015-12-30 01:34:43 +01:00
if (!res.isDone)
ResolutionError.MaximumIterationsReached
.throwException()
2015-12-30 01:34:45 +01:00
if (res.conflicts.nonEmpty) {
val projCache = res.projectCache.mapValues { case (_, p) => p }
2015-12-30 01:34:43 +01:00
ResolutionError.Conflicts(
"Conflict(s) in dependency resolution:\n " +
Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache)
).throwException()
}
2015-12-30 01:34:39 +01:00
if (res.errors.nonEmpty) {
val internalRepositoriesLen = internalRepositories.length
val errors =
if (repositories.length > internalRepositoriesLen)
// drop internal repository errors
res.errors.map {
case (dep, errs) =>
dep -> errs.drop(internalRepositoriesLen)
}
else
res.errors
2016-04-05 16:24:39 +02:00
ResolutionError.MetadataDownloadErrors(errors)
.throwException()
}
2015-12-30 01:34:39 +01:00
if (verbosityLevel >= 0)
log.info(s"Resolved ${projectDescription(currentProject)} dependencies")
2015-12-30 01:34:39 +01:00
res
} finally {
if (pool != null)
pool.shutdown()
if (resLogger != null)
resLogger.stop()
}
2016-05-06 13:53:49 +02:00
}
2016-01-10 21:32:28 +01:00
2016-05-06 13:53:49 +02:00
resolutionsCache.getOrElseUpdate(
ResolutionCacheKey(
currentProject,
repositories,
2016-08-04 01:36:53 +02:00
userEnabledProfiles,
startRes.copy(filter = None, profileActivation = None),
2016-05-06 13:53:49 +02:00
sbtClassifiers
),
resolution
)
}
}
2016-01-10 21:32:28 +01:00
2016-05-06 13:53:49 +02:00
def updateTask(
withClassifiers: Boolean,
sbtClassifiers: Boolean = false,
ignoreArtifactErrors: Boolean = false
) = Def.task {
2016-01-10 21:32:28 +01:00
2016-05-06 13:53:49 +02:00
def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
map.groupBy { case (k, _) => k }.map {
case (k, l) =>
k -> l.map { case (_, v) => v }
}
// let's update only one module at once, for a better output
// Downloads are already parallel, no need to parallelize further anyway
synchronized {
lazy val cm = coursierSbtClassifiersModule.value
val currentProject =
if (sbtClassifiers) {
val sv = scalaVersion.value
val sbv = scalaBinaryVersion.value
val defaultArtifactType = coursierDefaultArtifactType.value
2016-05-06 13:53:49 +02:00
FromSbt.project(
cm.id,
cm.modules,
cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap,
sv,
sbv,
defaultArtifactType
2016-05-06 13:53:49 +02:00
)
} else {
val proj = coursierProject.value
val publications = coursierPublications.value
proj.copy(publications = publications)
2016-01-10 21:32:28 +01:00
}
2016-05-06 13:53:49 +02:00
val ivySbt0 = ivySbt.value
val ivyCacheManager = ivySbt0.withIvy(streams.value.log)(ivy =>
ivy.getResolutionCacheManager
)
val ivyModule = ModuleRevisionId.newInstance(
currentProject.module.organization,
currentProject.module.name,
currentProject.version,
currentProject.module.attributes.asJava
)
val cacheIvyFile = ivyCacheManager.getResolvedIvyFileInCache(ivyModule)
val cacheIvyPropertiesFile = ivyCacheManager.getResolvedIvyPropertiesInCache(ivyModule)
val parallelDownloads = coursierParallelDownloads.value
val artifactsChecksums = coursierArtifactsChecksums.value
val cachePolicies = coursierCachePolicies.value
2016-05-31 15:18:29 +02:00
val ttl = coursierTtl.value
2016-05-06 13:53:49 +02:00
val cache = coursierCache.value
val log = streams.value.log
val verbosityLevel = coursierVerbosity.value
// required for publish to be fine, later on
def writeIvyFiles() = {
val printer = new scala.xml.PrettyPrinter(80, 2)
val b = new StringBuilder
b ++= """<?xml version="1.0" encoding="UTF-8"?>"""
b += '\n'
b ++= printer.format(MakeIvyXml(currentProject))
cacheIvyFile.getParentFile.mkdirs()
Files.write(cacheIvyFile.toPath, b.result().getBytes("UTF-8"))
// Just writing an empty file here... Are these only used?
cacheIvyPropertiesFile.getParentFile.mkdirs()
Files.write(cacheIvyPropertiesFile.toPath, "".getBytes("UTF-8"))
}
val res = {
if (withClassifiers && sbtClassifiers)
coursierSbtClassifiersResolution
else
coursierResolution
}.value
def report = {
var pool: ExecutorService = null
var artifactsLogger: TermDisplay = null
2016-05-06 13:53:49 +02:00
try {
pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
2016-05-06 13:53:49 +02:00
val depsByConfig = grouped(currentProject.dependencies)
2016-05-06 13:53:49 +02:00
val configs = coursierConfigurations.value
2016-05-06 13:53:49 +02:00
if (verbosityLevel >= 2) {
val finalDeps = Config.dependenciesWithConfig(
res,
depsByConfig.map { case (k, l) => k -> l.toSet },
configs
)
2016-01-10 21:32:28 +01:00
val projCache = res.projectCache.mapValues { case (_, p) => p }
val repr = Print.dependenciesUnknownConfigs(finalDeps.toVector, projCache)
log.info(repr.split('\n').map(" " + _).mkString("\n"))
}
2015-12-30 01:34:39 +01:00
val classifiers =
if (withClassifiers)
Some {
if (sbtClassifiers)
cm.classifiers
else
transitiveClassifiers.value
}
else
None
val allArtifacts =
classifiers match {
case None => res.artifacts
case Some(cl) => res.classifiersArtifacts(cl)
2015-12-30 01:34:43 +01:00
}
artifactsLogger = createLogger()
val artifactFileOrErrorTasks = allArtifacts.toVector.map { a =>
def f(p: CachePolicy) =
Cache.file(
a,
cache,
p,
checksums = artifactsChecksums,
logger = Some(artifactsLogger),
pool = pool,
ttl = ttl
)
cachePolicies.tail
.foldLeft(f(cachePolicies.head))(_ orElse f(_))
.run
.map((a, _))
2015-12-30 01:34:39 +01:00
}
if (verbosityLevel >= 0)
log.info(
s"Fetching artifacts of ${projectDescription(currentProject)}" +
(if (sbtClassifiers) " (sbt classifiers)" else "")
)
artifactsLogger.init()
2015-12-30 01:34:39 +01:00
val artifactFilesOrErrors = Task.gatherUnordered(artifactFileOrErrorTasks).attemptRun match {
case -\/(ex) =>
ResolutionError.UnknownDownloadException(ex)
.throwException()
case \/-(l) =>
l.toMap
}
2015-12-30 01:34:48 +01:00
if (verbosityLevel >= 0)
log.info(
s"Fetched artifacts of ${projectDescription(currentProject)}" +
(if (sbtClassifiers) " (sbt classifiers)" else "")
)
2015-12-30 01:34:39 +01:00
val artifactFiles = artifactFilesOrErrors.collect {
case (artifact, \/-(file)) =>
artifact -> file
}
2015-12-30 01:34:48 +01:00
val artifactErrors = artifactFilesOrErrors.toVector.collect {
case (_, -\/(err)) =>
err
}
2015-12-30 01:34:43 +01:00
if (artifactErrors.nonEmpty) {
val error = ResolutionError.DownloadErrors(artifactErrors)
if (ignoreArtifactErrors)
log.warn(error.description(verbosityLevel >= 1))
else
error.throwException()
}
// can be non empty only if ignoreArtifactErrors is true
val erroredArtifacts = artifactFilesOrErrors.collect {
case (artifact, -\/(_)) =>
artifact
}.toSet
def artifactFileOpt(artifact: Artifact) = {
val artifact0 = artifact
.copy(attributes = Attributes()) // temporary hack :-(
val res = artifactFiles.get(artifact0)
if (res.isEmpty && !erroredArtifacts(artifact0))
log.error(s"${artifact.url} not downloaded (should not happen)")
res
}
writeIvyFiles()
ToSbt.updateReport(
depsByConfig,
res,
configs,
classifiers,
artifactFileOpt
)
} finally {
if (pool != null)
pool.shutdown()
if (artifactsLogger != null)
artifactsLogger.stop()
2015-12-30 01:34:39 +01:00
}
}
2016-05-06 13:53:49 +02:00
reportsCache.getOrElseUpdate(
ReportCacheKey(
currentProject,
2016-08-04 01:36:53 +02:00
res.copy(filter = None, profileActivation = None),
withClassifiers,
sbtClassifiers
),
2015-12-30 01:34:43 +01:00
report
2015-12-30 01:34:39 +01:00
)
}
}
2016-05-06 13:53:49 +02:00
def coursierDependencyTreeTask(
inverse: Boolean,
sbtClassifiers: Boolean = false,
ignoreArtifactErrors: Boolean = false
) = Def.task {
val currentProject =
if (sbtClassifiers) {
val cm = coursierSbtClassifiersModule.value
val sv = scalaVersion.value
val sbv = scalaBinaryVersion.value
val defaultArtifactType = coursierDefaultArtifactType.value
2016-05-06 13:53:49 +02:00
FromSbt.project(
cm.id,
cm.modules,
cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap,
sv,
sbv,
defaultArtifactType
2016-05-06 13:53:49 +02:00
)
} else {
val proj = coursierProject.value
val publications = coursierPublications.value
proj.copy(publications = publications)
}
val res = {
if (sbtClassifiers)
coursierSbtClassifiersResolution
else
coursierResolution
}.value
val config = classpathConfiguration.value.name
val configs = coursierConfigurations.value
val includedConfigs = configs.getOrElse(config, Set.empty) + config
val dependencies0 = currentProject.dependencies.collect {
case (cfg, dep) if includedConfigs(cfg) => dep
}.sortBy { dep =>
(dep.module.organization, dep.module.name, dep.version)
}
val subRes = res.subset(dependencies0.toSet)
// use sbt logging?
println(
projectDescription(currentProject) + "\n" +
Print.dependencyTree(dependencies0, subRes, printExclusions = true, inverse)
)
}
def coursierExportTask =
(
sbt.Keys.state,
sbt.Keys.thisProjectRef,
sbt.Keys.projectID,
sbt.Keys.scalaVersion,
sbt.Keys.scalaBinaryVersion,
sbt.Keys.ivyConfigurations,
streams,
coursierProject,
coursierExportDirectory,
coursierExportJavadoc,
coursierExportSources
).flatMap { (state, projectRef, projId, sv, sbv, ivyConfs, streams, proj, exportDir, exportJavadoc, exportSources) =>
val javadocPackageTasks =
if (exportJavadoc)
Seq(Some("javadoc") -> packageDoc)
else
Nil
val sourcesPackageTasks =
if (exportJavadoc)
Seq(Some("sources") -> packageSrc)
else
Nil
val packageTasks = Seq(None -> packageBin) ++ javadocPackageTasks ++ sourcesPackageTasks
val configs = Seq(None -> Compile, Some("tests") -> Test)
val productTasks =
for {
(classifierOpt, pkgTask) <- packageTasks
(classifierPrefixOpt, config) <- configs
if publishArtifact.in(projectRef).in(pkgTask).in(config).getOrElse(state, false)
} yield {
val classifier = (classifierPrefixOpt.toSeq ++ classifierOpt.toSeq).mkString("-")
pkgTask.in(projectRef).in(config).get(state).map((classifier, _))
}
val productTask = sbt.std.TaskExtra.joinTasks(productTasks).join
val dir = new File(
exportDir,
s"${proj.module.organization.replace('.', '/')}/${proj.module.name}/${proj.version}"
)
def pom = "<?xml version='1.0' encoding='UTF-8'?>\n" + WritePom.project(proj, Some("jar"))
val log = streams.log
productTask.map { products =>
if (products.isEmpty)
None
else {
dir.mkdirs()
val pomFile = new File(dir, s"${proj.module.name}-${proj.version}.pom")
Files.write(pomFile.toPath, pom.getBytes("UTF-8"))
log.info(s"Wrote POM file to $pomFile")
for ((classifier, f) <- products) {
val suffix = if (classifier.isEmpty) "" else "-" + classifier
val jarPath = new File(dir, s"${proj.module.name}-${proj.version}$suffix.jar")
if (jarPath.exists()) {
if (!jarPath.delete())
log.warn(s"Cannot remove $jarPath")
}
Files.createSymbolicLink(
jarPath.toPath,
dir.toPath.relativize(f.toPath)
)
log.info(s"Created symbolic link $jarPath -> $f")
}
// TODO Clean extra files in dir
Some(exportDir)
}
}
}
2015-12-30 01:34:34 +01:00
}