scala.build.Artifacts.scala Maven / Gradle / Ivy
package scala.build
import coursier.cache.FileCache
import coursier.core.{Classifier, Module}
import coursier.parse.RepositoryParser
import coursier.util.Task
import coursier.{Dependency as CsDependency, Fetch, core as csCore, util as csUtil}
import dependency.*
import java.net.URL
import scala.build.CoursierUtils.*
import scala.build.EitherCps.{either, value}
import scala.build.Ops.*
import scala.build.errors.{
BuildException,
CompositeBuildException,
FetchingDependenciesError,
RepositoryFormatError
}
import scala.build.internal.Constants
import scala.build.internal.Constants.*
import scala.build.internal.CsLoggerUtil.*
import scala.build.internal.Util.ScalaDependencyOps
final case class Artifacts(
compilerDependencies: Seq[AnyDependency],
compilerArtifacts: Seq[(String, os.Path)],
compilerPlugins: Seq[(AnyDependency, String, os.Path)],
javacPluginDependencies: Seq[(AnyDependency, String, os.Path)],
extraJavacPlugins: Seq[os.Path],
userDependencies: Seq[AnyDependency],
internalDependencies: Seq[AnyDependency],
scalaJsCli: Seq[os.Path],
scalaNativeCli: Seq[os.Path],
detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)],
extraClassPath: Seq[os.Path],
extraCompileOnlyJars: Seq[os.Path],
extraSourceJars: Seq[os.Path],
params: ScalaParameters
) {
lazy val artifacts: Seq[(String, os.Path)] =
detailedArtifacts
.iterator
.collect {
case (_, pub, a, f) if pub.classifier != Classifier.sources =>
(a.url, f)
}
.toVector
lazy val sourceArtifacts: Seq[(String, os.Path)] =
detailedArtifacts
.iterator
.collect {
case (_, pub, a, f) if pub.classifier == Classifier.sources =>
(a.url, f)
}
.toVector
lazy val compilerClassPath: Seq[os.Path] =
compilerArtifacts.map(_._2)
lazy val classPath: Seq[os.Path] =
artifacts.map(_._2) ++ extraClassPath
lazy val compileClassPath: Seq[os.Path] =
artifacts.map(_._2) ++ extraClassPath ++ extraCompileOnlyJars
lazy val sourcePath: Seq[os.Path] =
sourceArtifacts.map(_._2) ++ extraSourceJars
}
object Artifacts {
def apply(
params: ScalaParameters,
compilerPlugins: Seq[Positioned[AnyDependency]],
javacPluginDependencies: Seq[Positioned[AnyDependency]],
extraJavacPlugins: Seq[os.Path],
dependencies: Seq[Positioned[AnyDependency]],
extraClassPath: Seq[os.Path],
extraCompileOnlyJars: Seq[os.Path],
extraSourceJars: Seq[os.Path],
fetchSources: Boolean,
addStubs: Boolean,
addJvmRunner: Option[Boolean],
addJvmTestRunner: Boolean,
addJsTestBridge: Option[String],
addNativeTestInterface: Option[String],
addJmhDependencies: Option[String],
scalaJsCliVersion: Option[String],
scalaNativeCliVersion: Option[String],
extraRepositories: Seq[String],
cache: FileCache[Task],
logger: Logger
): Either[BuildException, Artifacts] = either {
val compilerDependencies =
if (params.scalaVersion.startsWith("3."))
Seq(
dep"org.scala-lang::scala3-compiler:${params.scalaVersion}"
)
else
Seq(
dep"org.scala-lang:scala-compiler:${params.scalaVersion}"
)
val compilerDependenciesMessage =
s"Downloading Scala ${params.scalaVersion} compiler"
val jvmRunnerDependencies =
if (addJvmRunner.getOrElse(true))
Seq(dep"$runnerOrganization::$runnerModuleName:$runnerVersion")
else
Nil
val jvmTestRunnerDependencies =
if (addJvmTestRunner)
Seq(dep"$testRunnerOrganization::$testRunnerModuleName:$testRunnerVersion")
else
Nil
val jsTestBridgeDependencies = addJsTestBridge.toSeq.map { scalaJsVersion =>
if (params.scalaVersion.startsWith("2."))
dep"org.scala-js::scalajs-test-bridge:$scalaJsVersion"
else
dep"org.scala-js:scalajs-test-bridge_2.13:$scalaJsVersion"
}
val nativeTestInterfaceDependencies = addNativeTestInterface.toSeq.map { scalaNativeVersion =>
dep"org.scala-native::test-interface::$scalaNativeVersion"
}
val jmhDependencies = addJmhDependencies.toSeq.map { version =>
dep"org.openjdk.jmh:jmh-generator-bytecode:$version"
}
val maybeSnapshotRepo = {
val hasSnapshots = (jvmRunnerDependencies ++ jvmTestRunnerDependencies)
.exists(_.version.endsWith("SNAPSHOT")) ||
scalaNativeCliVersion.exists(_.endsWith("SNAPSHOT"))
val runnerNeedsSonatypeSnapshots = Constants.runnerNeedsSonatypeSnapshots(params.scalaVersion)
val stubsNeedSonatypeSnapshots = addStubs && stubsVersion.endsWith("SNAPSHOT")
if (hasSnapshots || runnerNeedsSonatypeSnapshots || stubsNeedSonatypeSnapshots)
Seq(coursier.Repositories.sonatype("snapshots").root)
else
Nil
}
val scalaJsCliDependency =
scalaJsCliVersion.map { version =>
val mod =
if (version.contains("-sc")) cmod"io.github.alexarchambault.tmp:scalajs-cli_2.13"
else cmod"org.scala-js:scalajs-cli_2.13"
Seq(coursier.Dependency(mod, version))
}
val scalaNativeCliDependency =
scalaNativeCliVersion.map { version =>
val module = cmod"org.scala-native:scala-native-cli_2.12"
Seq(coursier.Dependency(module, version))
}
val allExtraRepositories =
maybeSnapshotRepo ++ extraRepositories
val internalDependencies =
jvmRunnerDependencies.map(Positioned.none) ++
jvmTestRunnerDependencies.map(Positioned.none) ++
jsTestBridgeDependencies.map(Positioned.none) ++
nativeTestInterfaceDependencies.map(Positioned.none) ++
jmhDependencies.map(Positioned.none)
val updatedDependencies = dependencies ++ internalDependencies
val updatedDependenciesMessage = {
val b = new StringBuilder("Downloading ")
val depLen = dependencies.length
val extraDepLen = updatedDependencies.length - depLen
depLen match {
case 1 => b.append("one dependency")
case n if n > 1 => b.append(s"$n dependencies")
case _ =>
}
if (depLen > 0 && extraDepLen > 0)
b.append(" and ")
extraDepLen match {
case 1 => b.append("one internal dependency")
case n if n > 1 => b.append(s"$n internal dependencies")
case _ =>
}
b.result()
}
val compilerArtifacts = value {
artifacts(
Positioned.none(compilerDependencies),
allExtraRepositories,
params,
logger,
cache.withMessage(compilerDependenciesMessage)
)
}
val fetchRes = value {
fetch(
Positioned.sequence(updatedDependencies),
allExtraRepositories,
params,
logger,
cache.withMessage(updatedDependenciesMessage),
classifiersOpt = Some(Set("_") ++ (if (fetchSources) Set("sources") else Set.empty))
)
}
val fetchedScalaNativeCli = scalaNativeCliDependency match {
case Some(dependency) =>
Some(
value {
fetch0(
Positioned.none(dependency),
allExtraRepositories,
None,
Nil,
logger,
cache.withMessage("Downloading Scala Native CLI"),
None
)
}
)
case None =>
None
}
def fetchedArtifactToPath(fetched: Fetch.Result): Seq[os.Path] =
fetched.fullDetailedArtifacts.collect { case (_, _, _, Some(f)) => os.Path(f, Os.pwd) }
val scalaNativeCli = fetchedScalaNativeCli.toSeq.flatMap(fetchedArtifactToPath)
val fetchedScalaJsCli = scalaJsCliDependency match {
case Some(dependency) =>
val forcedVersions = Seq(
cmod"org.scala-js:scalajs-linker_2.13" -> scalaJsVersion
)
Some(
value {
fetch0(
Positioned.none(dependency),
allExtraRepositories,
None,
forcedVersions,
logger,
cache.withMessage("Downloading Scala.js CLI"),
None
)
}
)
case None =>
None
}
val scalaJsCli = fetchedScalaJsCli.toSeq.flatMap(fetchedArtifactToPath)
val extraStubsJars =
if (addStubs)
value {
artifacts(
Positioned.none(Seq(dep"$stubsOrganization:$stubsModuleName:$stubsVersion")),
allExtraRepositories,
params,
logger,
cache.withMessage("Downloading internal stub dependency")
).map(_.map(_._2))
}
else
Nil
val compilerPlugins0 = value {
compilerPlugins
.map { posDep =>
val posDep0 =
posDep.map(dep => dep.copy(userParams = dep.userParams + ("intransitive" -> None)))
artifacts(
posDep0.map(Seq(_)),
allExtraRepositories,
params,
logger,
cache.withMessage(s"Downloading compiler plugin ${posDep.value.render}")
).map(_.map { case (url, path) => (posDep0.value, url, path) })
}
.sequence
.left.map(CompositeBuildException(_))
.map(_.flatten)
}
val javacPlugins0 = value {
javacPluginDependencies
.map { posDep =>
val cache0 = cache.withMessage(s"Downloading javac plugin ${posDep.value.render}")
artifacts(posDep.map(Seq(_)), allExtraRepositories, params, logger, cache0)
.map(_.map { case (url, path) => (posDep.value, url, path) })
}
.sequence
.left.map(CompositeBuildException(_))
.map(_.flatten)
}
Artifacts(
compilerDependencies,
compilerArtifacts,
compilerPlugins0,
javacPlugins0,
extraJavacPlugins,
dependencies.map(_.value),
internalDependencies.map(_.value),
scalaJsCli,
scalaNativeCli,
fetchRes.fullDetailedArtifacts.collect { case (d, p, a, Some(f)) =>
(d, p, a, os.Path(f, Os.pwd))
},
extraClassPath ++ extraStubsJars,
extraCompileOnlyJars,
extraSourceJars,
params
)
}
private[build] def artifacts(
dependencies: Positioned[Seq[AnyDependency]],
extraRepositories: Seq[String],
params: ScalaParameters,
logger: Logger,
cache: FileCache[Task],
classifiersOpt: Option[Set[String]] = None
): Either[BuildException, Seq[(String, os.Path)]] = either {
val res = value(fetch(dependencies, extraRepositories, params, logger, cache, classifiersOpt))
val result = res
.artifacts
.iterator
.map { case (a, f) => (a.url, os.Path(f, Os.pwd)) }
.toList
logger.debug {
val elems = Seq(s"Found ${result.length} artifacts:") ++
result.map(" " + _._2) ++
Seq("")
elems.mkString(System.lineSeparator())
}
result
}
def fetch(
dependencies: Positioned[Seq[AnyDependency]],
extraRepositories: Seq[String],
params: ScalaParameters,
logger: Logger,
cache: FileCache[Task],
classifiersOpt: Option[Set[String]]
): Either[BuildException, Fetch.Result] = {
val coursierDependenciesWithFallbacks
: Positioned[Seq[(CsDependency, Option[((Module, String), (URL, Boolean))])]] =
dependencies.map(positioned =>
for {
dep <- positioned
csDep = dep.toCs(params)
maybeUrl = dep.userParams.get("url").flatten.map(new URL(_))
fallback = maybeUrl.map(url => (csDep.module -> csDep.version) -> (url -> true))
} yield csDep -> fallback
)
val coursierDependencies: Positioned[Seq[CsDependency]] =
coursierDependenciesWithFallbacks.map(_.map(_._1))
val fallbacks: Map[(Module, String), (URL, Boolean)] =
coursierDependenciesWithFallbacks.value.flatMap(_._2).toMap
fetch0(
coursierDependencies,
extraRepositories,
Some(params.scalaVersion),
Nil,
logger,
cache,
classifiersOpt,
fallbacks
)
}
def fetch0(
dependencies: Positioned[Seq[coursier.Dependency]],
extraRepositories: Seq[String],
forceScalaVersionOpt: Option[String],
forcedVersions: Seq[(coursier.Module, String)],
logger: Logger,
cache: FileCache[Task],
classifiersOpt: Option[Set[String]],
fallbacks: Map[(Module, String), (URL, Boolean)] = Map.empty
): Either[BuildException, Fetch.Result] = either {
logger.debug {
s"Fetching ${dependencies.value}" +
(if (extraRepositories.isEmpty) "" else s", adding $extraRepositories")
}
val fallbackRepository = TemporaryInMemoryRepository(fallbacks)
val extraRepositories0 = value {
RepositoryParser.repositories(extraRepositories)
.either
.left.map(errors => new RepositoryFormatError(errors))
}
val extraRepositoriesWithFallback = extraRepositories0 :+ fallbackRepository
val forceScalaVersions = forceScalaVersionOpt match {
case None => Nil
case Some(sv) =>
if (sv.startsWith("2."))
Seq(
cmod"org.scala-lang:scala-library" -> sv,
cmod"org.scala-lang:scala-compiler" -> sv,
cmod"org.scala-lang:scala-reflect" -> sv
)
else
// FIXME Shouldn't we force the org.scala-lang:scala-library version too?
// (to a 2.13.x version)
Seq(
cmod"org.scala-lang:scala3-library_3" -> sv,
cmod"org.scala-lang:scala3-compiler_3" -> sv,
cmod"org.scala-lang:scala3-interfaces_3" -> sv,
cmod"org.scala-lang:scala3-tasty-inspector_3" -> sv,
cmod"org.scala-lang:tasty-core_3" -> sv
)
}
val forceVersion = forceScalaVersions ++ forcedVersions
// FIXME Many parameters that we could allow to customize here
var fetcher = coursier.Fetch()
.withCache(cache)
.addRepositories(extraRepositoriesWithFallback*)
.addDependencies(dependencies.value*)
.mapResolutionParams(_.addForceVersion(forceVersion*))
for (classifiers <- classifiersOpt) {
if (classifiers("_"))
fetcher = fetcher.withMainArtifacts()
fetcher = fetcher
.addClassifiers(classifiers.toSeq.filter(_ != "_").map(coursier.Classifier(_))*)
}
val res = cache.logger.use {
fetcher.eitherResult()
}
value {
res.left.map(ex => new FetchingDependenciesError(ex, dependencies.positions))
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy