sbt.internal.ClasspathImpl.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of main_2.12 Show documentation
Show all versions of main_2.12 Show documentation
sbt is an interactive build tool
The newest version!
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
import java.io.File
import java.util.LinkedHashSet
import sbt.SlashSyntax0._
import sbt.Keys._
import sbt.nio.Keys._
import sbt.nio.file.{ Glob, RecursiveGlob }
import sbt.Def.Initialize
import sbt.internal.inc.Analysis
import sbt.internal.inc.JavaInterfaceUtil._
import sbt.internal.util.{ Attributed, Dag, Settings }
import sbt.librarymanagement.{ Configuration, TrackLevel }
import sbt.librarymanagement.Configurations.names
import sbt.std.TaskExtra._
import sbt.util._
import scala.collection.JavaConverters._
import xsbti.compile.CompileAnalysis
private[sbt] object ClasspathImpl {
// Since we can't predict the path for pickleProduct,
// we can't reduce the track level.
def exportedPicklesTask: Initialize[Task[VirtualClasspath]] =
Def.task {
// conditional task: do not refactor
if (exportPipelining.value) {
val module = projectID.value
val config = configuration.value
val products = pickleProducts.value
val analysis = compileEarly.value
val xs = products map { _ -> analysis }
for { (f, analysis) <- xs } yield APIMappings
.store(analyzed(f, analysis), apiURL.value)
.put(moduleID.key, module)
.put(configuration.key, config)
} else {
val c = fileConverter.value
val ps = exportedProducts.value
ps.map(attr => attr.map(x => c.toVirtualFile(x.toPath)))
}
}
def trackedExportedProducts(track: TrackLevel): Initialize[Task[Classpath]] =
Def.task {
val _ = (packageBin / dynamicDependency).value
val art = (packageBin / artifact).value
val module = projectID.value
val config = configuration.value
for { (f, analysis) <- trackedExportedProductsImplTask(track).value } yield APIMappings
.store(analyzed(f, analysis), apiURL.value)
.put(artifact.key, art)
.put(moduleID.key, module)
.put(configuration.key, config)
}
def trackedExportedJarProducts(track: TrackLevel): Initialize[Task[Classpath]] =
Def.task {
val _ = (packageBin / dynamicDependency).value
val art = (packageBin / artifact).value
val module = projectID.value
val config = configuration.value
for { (f, analysis) <- trackedJarProductsImplTask(track).value } yield APIMappings
.store(analyzed(f, analysis), apiURL.value)
.put(artifact.key, art)
.put(moduleID.key, module)
.put(configuration.key, config)
}
private[this] def trackedExportedProductsImplTask(
track: TrackLevel
): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
Def.taskDyn {
val _ = (packageBin / dynamicDependency).value
val useJars = exportJars.value
if (useJars) trackedJarProductsImplTask(track)
else trackedNonJarProductsImplTask(track)
}
private[this] def trackedNonJarProductsImplTask(
track: TrackLevel
): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
Def.taskDyn {
val dirs = productDirectories.value
val view = fileTreeView.value
def containsClassFile(): Boolean =
view.list(dirs.map(Glob(_, RecursiveGlob / "*.class"))).nonEmpty
TrackLevel.intersection(track, exportToInternal.value) match {
case TrackLevel.TrackAlways =>
Def.task {
products.value map { (_, compile.value) }
}
case TrackLevel.TrackIfMissing if !containsClassFile() =>
Def.task {
products.value map { (_, compile.value) }
}
case _ =>
Def.task {
val analysis = previousCompile.value.analysis.toOption.getOrElse(Analysis.empty)
dirs.map(_ -> analysis)
}
}
}
private[this] def trackedJarProductsImplTask(
track: TrackLevel
): Initialize[Task[Seq[(File, CompileAnalysis)]]] =
Def.taskDyn {
val jar = (packageBin / artifactPath).value
TrackLevel.intersection(track, exportToInternal.value) match {
case TrackLevel.TrackAlways =>
Def.task {
Seq((packageBin.value, compile.value))
}
case TrackLevel.TrackIfMissing if !jar.exists =>
Def.task {
Seq((packageBin.value, compile.value))
}
case _ =>
Def.task {
val analysisOpt = previousCompile.value.analysis.toOption
Seq(jar) map { x =>
(
x,
if (analysisOpt.isDefined) analysisOpt.get
else Analysis.empty
)
}
}
}
}
def internalDependencyClasspathTask: Initialize[Task[Classpath]] = {
Def.taskDyn {
val _ = (
(exportedProductsNoTracking / transitiveClasspathDependency).value,
(exportedProductsIfMissing / transitiveClasspathDependency).value,
(exportedProducts / transitiveClasspathDependency).value,
(exportedProductJarsNoTracking / transitiveClasspathDependency).value,
(exportedProductJarsIfMissing / transitiveClasspathDependency).value,
(exportedProductJars / transitiveClasspathDependency).value
)
internalDependenciesImplTask(
thisProjectRef.value,
classpathConfiguration.value,
configuration.value,
settingsData.value,
buildDependencies.value,
trackInternalDependencies.value,
streams.value.log,
)
}
}
def internalDependenciesImplTask(
projectRef: ProjectRef,
conf: Configuration,
self: Configuration,
data: Settings[Scope],
deps: BuildDependencies,
track: TrackLevel,
log: Logger
): Initialize[Task[Classpath]] =
Def.value {
interDependencies(projectRef, deps, conf, self, data, track, false, log)(
exportedProductsNoTracking,
exportedProductsIfMissing,
exportedProducts
)
}
def internalDependencyPicklePathTask: Initialize[Task[VirtualClasspath]] = {
def implTask(
projectRef: ProjectRef,
conf: Configuration,
self: Configuration,
data: Settings[Scope],
deps: BuildDependencies,
track: TrackLevel,
log: Logger
): Initialize[Task[VirtualClasspath]] =
Def.value {
interDependencies(projectRef, deps, conf, self, data, track, false, log)(
exportedPickles,
exportedPickles,
exportedPickles
)
}
Def.taskDyn {
implTask(
thisProjectRef.value,
classpathConfiguration.value,
configuration.value,
settingsData.value,
buildDependencies.value,
TrackLevel.TrackAlways,
streams.value.log,
)
}
}
def internalDependencyJarsTask: Initialize[Task[Classpath]] =
Def.taskDyn {
internalDependencyJarsImplTask(
thisProjectRef.value,
classpathConfiguration.value,
configuration.value,
settingsData.value,
buildDependencies.value,
trackInternalDependencies.value,
streams.value.log,
)
}
private def internalDependencyJarsImplTask(
projectRef: ProjectRef,
conf: Configuration,
self: Configuration,
data: Settings[Scope],
deps: BuildDependencies,
track: TrackLevel,
log: Logger
): Initialize[Task[Classpath]] =
Def.value {
interDependencies(projectRef, deps, conf, self, data, track, false, log)(
exportedProductJarsNoTracking,
exportedProductJarsIfMissing,
exportedProductJars
)
}
def unmanagedDependenciesTask: Initialize[Task[Classpath]] =
Def.taskDyn {
unmanagedDependencies0(
thisProjectRef.value,
configuration.value,
settingsData.value,
buildDependencies.value,
streams.value.log
)
}
def unmanagedDependencies0(
projectRef: ProjectRef,
conf: Configuration,
data: Settings[Scope],
deps: BuildDependencies,
log: Logger
): Initialize[Task[Classpath]] =
Def.value {
interDependencies(
projectRef,
deps,
conf,
conf,
data,
TrackLevel.TrackAlways,
true,
log
)(
unmanagedJars,
unmanagedJars,
unmanagedJars
)
}
def unmanagedLibs(
dep: ResolvedReference,
conf: String,
data: Settings[Scope]
): Task[Classpath] =
getClasspath(unmanagedJars, dep, conf, data)
def interDependencies[A](
projectRef: ProjectRef,
deps: BuildDependencies,
conf: Configuration,
self: Configuration,
data: Settings[Scope],
track: TrackLevel,
includeSelf: Boolean,
log: Logger
)(
noTracking: TaskKey[Seq[A]],
trackIfMissing: TaskKey[Seq[A]],
trackAlways: TaskKey[Seq[A]]
): Task[Seq[A]] = {
val interDepConfigs = interSort(projectRef, conf, data, deps) filter {
case (dep, c) =>
includeSelf || (dep != projectRef) || (conf.name != c && self.name != c)
}
val tasks = (new LinkedHashSet[Task[Seq[A]]]).asScala
for {
(dep, c) <- interDepConfigs
} {
tasks += (track match {
case TrackLevel.NoTracking =>
getClasspath(noTracking, dep, c, data)
case TrackLevel.TrackIfMissing =>
getClasspath(trackIfMissing, dep, c, data)
case TrackLevel.TrackAlways =>
getClasspath(trackAlways, dep, c, data)
})
}
(tasks.toSeq.join).map(_.flatten.distinct)
}
def analyzed[A](data: A, analysis: CompileAnalysis) =
Attributed.blank(data).put(Keys.analysis, analysis)
def interSort(
projectRef: ProjectRef,
conf: Configuration,
data: Settings[Scope],
deps: BuildDependencies
): Seq[(ProjectRef, String)] = {
val visited = (new LinkedHashSet[(ProjectRef, String)]).asScala
def visit(p: ProjectRef, c: Configuration): Unit = {
val applicableConfigs = allConfigs(c)
for {
ac <- applicableConfigs
} // add all configurations in this project
visited add (p -> ac.name)
val masterConfs = names(getConfigurations(projectRef, data).toVector)
for {
ResolvedClasspathDependency(dep, confMapping) <- deps.classpath(p)
} {
val configurations = getConfigurations(dep, data)
val mapping =
mapped(confMapping, masterConfs, names(configurations.toVector), "compile", "*->compile")
// map master configuration 'c' and all extended configurations to the appropriate dependency configuration
for {
ac <- applicableConfigs
depConfName <- mapping(ac.name)
} {
for {
depConf <- confOpt(configurations, depConfName)
} if (!visited((dep, depConfName))) {
visit(dep, depConf)
}
}
}
}
visit(projectRef, conf)
visited.toSeq
}
def mapped(
confString: Option[String],
masterConfs: Seq[String],
depConfs: Seq[String],
default: String,
defaultMapping: String
): String => Seq[String] = {
lazy val defaultMap = parseMapping(defaultMapping, masterConfs, depConfs, _ :: Nil)
parseMapping(confString getOrElse default, masterConfs, depConfs, defaultMap)
}
def parseMapping(
confString: String,
masterConfs: Seq[String],
depConfs: Seq[String],
default: String => Seq[String]
): String => Seq[String] =
union(confString.split(";") map parseSingleMapping(masterConfs, depConfs, default))
def parseSingleMapping(
masterConfs: Seq[String],
depConfs: Seq[String],
default: String => Seq[String]
)(confString: String): String => Seq[String] = {
val ms: Seq[(String, Seq[String])] =
trim(confString.split("->", 2)) match {
case x :: Nil => for (a <- parseList(x, masterConfs)) yield (a, default(a))
case x :: y :: Nil =>
val target = parseList(y, depConfs);
for (a <- parseList(x, masterConfs)) yield (a, target)
case _ => sys.error("Invalid configuration '" + confString + "'") // shouldn't get here
}
val m = ms.toMap
s => m.getOrElse(s, Nil)
}
def union[A, B](maps: Seq[A => Seq[B]]): A => Seq[B] =
a => maps.foldLeft(Seq[B]()) { _ ++ _(a) } distinct;
def parseList(s: String, allConfs: Seq[String]): Seq[String] =
(trim(s split ",") flatMap replaceWildcard(allConfs)).distinct
def replaceWildcard(allConfs: Seq[String])(conf: String): Seq[String] = conf match {
case "" => Nil
case "*" => allConfs
case _ => conf :: Nil
}
private def trim(a: Array[String]): List[String] = a.toList.map(_.trim)
def allConfigs(conf: Configuration): Seq[Configuration] =
Dag.topologicalSort(conf)(_.extendsConfigs)
def getConfigurations(p: ResolvedReference, data: Settings[Scope]): Seq[Configuration] =
(p / ivyConfigurations).get(data).getOrElse(Nil)
def confOpt(configurations: Seq[Configuration], conf: String): Option[Configuration] =
configurations.find(_.name == conf)
def getClasspath[A](
key: TaskKey[Seq[A]],
dep: ResolvedReference,
conf: Configuration,
data: Settings[Scope]
): Task[Seq[A]] = getClasspath(key, dep, conf.name, data)
def getClasspath[A](
key: TaskKey[Seq[A]],
dep: ResolvedReference,
conf: String,
data: Settings[Scope]
): Task[Seq[A]] =
(dep / ConfigKey(conf) / key).get(data) match {
case Some(x) => x
case _ => constant(Nil)
}
}