Skip to content

Commit

Permalink
Refactor dependencies (#187)
Browse files Browse the repository at this point in the history
* Support for `$SCALA_VERSION` in dependency versions

* Refactor dependencies

- `model.VersionScalaPlatform` => `model.VersionCombo`
- use `model.Dep` in `CoursierResolver` instead of `coursier.Dependency`
- refactor and move scala suffix calculation to `Dep` from `VersionScalaPlatform`

* towards ${PLATFORM_VERSION}

- have `Replacements` rewrite dependency versions as well
- collect replacement keys in Replacements companion
- don't infer ${SCALA_EPOCH}
- only support syntax with curly braces
  • Loading branch information
oyvindberg authored Aug 16, 2022
1 parent 54199d4 commit 803fa9b
Show file tree
Hide file tree
Showing 25 changed files with 398 additions and 568 deletions.
5 changes: 1 addition & 4 deletions bleep-cli/src/scala/bleep/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,7 @@ object Main {
buildLoader match {
case BuildLoader.NonExisting(_) => ()
case existing: BuildLoader.Existing =>
val maybeWantedVersion: Either[Exception, model.BleepVersion] =
existing.json.forceGet.flatMap(json => json.hcursor.downField("$version").as[model.BleepVersion])

maybeWantedVersion match {
existing.wantedVersion.forceGet match {
case Right(wantedVersion) if model.BleepVersion.current == wantedVersion || wantedVersion == model.BleepVersion.dev => ()
case Right(wantedVersion) if opts.dev =>
logger.info(s"Not launching Bleep version ${wantedVersion.value} (from ${existing.bleepYaml}) because you specified --dev")
Expand Down
6 changes: 3 additions & 3 deletions bleep-cli/src/scala/bleep/commands/BuildUpdateDeps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ object BuildUpdateDeps {
def instantiateAllDependencies(build: model.ExplodedBuild): Map[model.Dep, List[Dependency]] =
build.projects.toList
.flatMap { case (crossName, p) =>
model.VersionScalaPlatform.fromExplodedProject(p) match {
model.VersionCombo.fromExplodedProject(p) match {
case Left(err) =>
throw new BleepException.Text(crossName, err)
case Right(scalaPlatform) =>
case Right(versionCombo) =>
p.dependencies.values.iterator.collect {
case dep if !dep.version.contains("$") => (dep, dep.dependencyForce(crossName, scalaPlatform))
case dep if !dep.version.contains("$") => (dep, dep.asDependencyForce(Some(crossName), versionCombo))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion bleep-cli/src/scala/bleep/commands/Import.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ case class Import(

val files = generateBuild(
inputProjects,
bleepTasksVersion = model.BleepVersion(constants.BleepVersionTemplate),
bleepTasksVersion = model.BleepVersion(model.Replacements.known.BleepVersion),
generatedFiles,
existingBuild
).map { case (path, content) => (RelPath.relativeTo(destinationPaths.buildDir, path), content) }
Expand Down
30 changes: 15 additions & 15 deletions bleep-cli/src/scala/bleep/internal/importBloopFilesFromSbt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,16 @@ object importBloopFilesFromSbt {
model.Replacements.targetDir(originalTarget) ++
model.Replacements.scope(projectType.sbtScope)

val replacementsWithVersions = replacements ++ model.Replacements.versions(scalaVersion, bloopProject.platform.map(_.name), includeEpoch = true)

val configuredPlatform: Option[model.Platform] =
bloopProject.platform.map(translatePlatform(_, replacements, bloopProject.resolution))

val scalaPlatform = model.VersionScalaPlatform.fromExplodedScalaAndPlatform(scalaVersion, configuredPlatform) match {
val versionCombo = model.VersionCombo.fromExplodedScalaAndPlatform(scalaVersion, configuredPlatform) match {
case Left(value) => throw new BleepException.Text(crossName, value)
case Right(value) => value
}

val replacementsWithVersions = replacements ++ model.Replacements.versions(None, versionCombo, includeEpoch = false, includeBinVersion = true)

val sources: Sources = {
val sourcesRelPaths = {
val sources = bloopProject.sources.filterNot(_.startsWith(originalTarget))
Expand Down Expand Up @@ -129,16 +129,17 @@ object importBloopFilesFromSbt {
Sources(inferredSourceLayout, shortenedSourcesRelPaths, shortenedResourcesRelPaths)
}

val depReplacements = model.Replacements.versions(None, versionCombo, includeEpoch = false, includeBinVersion = false)
val (compilerPlugins, dependencies) = {
val providedDeps = scalaPlatform.libraries(isTest = projectType.testLike)
importDeps(logger, inputProject, crossName, configuredPlatform.flatMap(_.name), providedDeps)
val providedDeps = versionCombo.libraries(isTest = projectType.testLike)
importDeps(logger, inputProject, crossName, configuredPlatform.flatMap(_.name), providedDeps, depReplacements)
}

val configuredJava: Option[model.Java] =
bloopProject.java.map(translateJava(replacements))

val configuredScala: Option[model.Scala] =
bloopProject.scala.map(translateScala(compilerPlugins, replacements, scalaPlatform))
bloopProject.scala.map(translateScala(compilerPlugins, replacements, versionCombo, depReplacements))

val testFrameworks: model.JsonSet[model.TestFrameworkName] =
if (projectType.testLike) {
Expand Down Expand Up @@ -186,7 +187,8 @@ object importBloopFilesFromSbt {
inputProject: ImportInputProjects.InputProject,
crossName: model.CrossProjectName,
platformName: Option[model.PlatformId],
providedDeps: Seq[model.Dep]
providedDeps: Seq[model.Dep],
replacements: model.Replacements
): (Seq[model.Dep], Seq[model.Dep]) = {
// compare by string to ignore things like configuration
val providedDepReprs: Set[String] =
Expand All @@ -201,20 +203,18 @@ object importBloopFilesFromSbt {
None
case Right(dep) if providedDepReprs(dep.repr) =>
None
case Right(dep) =>
Some(dep)
case Right(dep) => Some(replacements.templatize.dep(dep))
}
}

val CompilerPluginConfig = Configuration("plugin->default(compile)")
val CompilerPluginScalaJsTest = Configuration("scala-js-test-plugin")
val plugins = all.collect {
case dep if dep.configuration == CompilerPluginConfig || (dep.configuration == CompilerPluginScalaJsTest && inputProject.projectType.testLike) =>
dep.withConfiguration(Configuration.empty) match {
case x: model.Dep.JavaDependency => x
dep
.withConfiguration(Configuration.empty)
// always true for compiler plugins. this is really just aesthetic in the generated json file
case x: model.Dep.ScalaDependency => x.copy(forceJvm = false)
}
.mapScala(_.copy(forceJvm = false))
}

val ItConf = Configuration("it")
Expand Down Expand Up @@ -338,7 +338,7 @@ object importBloopFilesFromSbt {
translatedPlatform
}

def translateScala(compilerPlugins: Seq[model.Dep], replacements: model.Replacements, scalaPlatform: model.VersionScalaPlatform)(
def translateScala(compilerPlugins: Seq[model.Dep], replacements: model.Replacements, versionCombo: model.VersionCombo, depReplacements: model.Replacements)(
s: Config.Scala
): model.Scala = {
val options = parseOptionsDropSemanticDb(s.options, Some(replacements))
Expand All @@ -350,7 +350,7 @@ object importBloopFilesFromSbt {

val filteredCompilerPlugins: Seq[model.Dep] =
compilerOptionsDropSemanticDb {
scalaPlatform.compilerPlugin.foldLeft(compilerPlugins) { case (all, fromPlatform) => all.filterNot(_ == fromPlatform) }
versionCombo.compilerPlugin.foldLeft(compilerPlugins) { case (all, fromPlatform) => all.filterNot(_ == depReplacements.templatize.dep(fromPlatform)) }
}

val (strict, remainingOptions) = {
Expand Down
5 changes: 1 addition & 4 deletions bleep-cli/src/scala/bleep/rewrites/unifyDeps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ object unifyDeps extends Rewrite {

build.copy(projects = build.projects.map { case (crossName, p) =>
val newP = p.copy(dependencies = p.dependencies.map { dep0 =>
val dep0ForceJvm = dep0 match {
case x: model.Dep.JavaDependency => x
case x: model.Dep.ScalaDependency => x.copy(forceJvm = true)
}
val dep0ForceJvm = dep0.mapScala(_.copy(forceJvm = true))

val dep1 = replacements(dep0)

Expand Down
9 changes: 5 additions & 4 deletions bleep-core/src/scala/bleep/BuildPaths.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package bleep

import java.nio.file.Path

case class BuildPaths(cwd: Path, bleepYamlFile: Path, mode: BuildPaths.Mode) {
case class BuildPaths(cwd: Path, bleepYamlFile: Path, mode: BuildPaths.Mode, wantedBleepVersion: Option[model.BleepVersion]) {
lazy val buildDir = bleepYamlFile.getParent
lazy val bspBleepJsonFile: Path = buildDir / ".bsp" / "bleep.json"
lazy val dotBleepDir: Path = buildDir / ".bleep"
Expand All @@ -24,12 +24,13 @@ case class BuildPaths(cwd: Path, bleepYamlFile: Path, mode: BuildPaths.Mode) {
val dir = buildDir / p.folder.getOrElse(RelPath.force(crossName.name.value))
val scalaVersion: Option[model.VersionScala] = p.scala.flatMap(_.version)
val maybePlatformId = p.platform.flatMap(_.name)
val targetDir = bleepBloopDir / crossName.name.value / crossName.crossId.fold("")(_.value)
val maybePlatformVersion = p.platform.flatMap(p => p.jsVersion.map(_.scalaJsVersion).orElse(p.nativeVersion.map(_.scalaNativeVersion)))

val targetDir = bleepBloopDir / crossName.name.value / crossName.crossId.fold("")(_.value)
val replacements = model.Replacements.paths(dir, buildDir) ++
model.Replacements.targetDir(targetDir) ++
model.Replacements.scope(p.`sbt-scope`.getOrElse("")) ++
model.Replacements.versions(scalaVersion, maybePlatformId.map(_.value), includeEpoch = true)
model.Replacements.versions(wantedBleepVersion, scalaVersion, maybePlatformId, maybePlatformVersion, includeEpoch = true, includeBinVersion = true)

def sourceLayout = p.`source-layout` match {
case Some(sourceLayout) => sourceLayout
Expand Down Expand Up @@ -71,5 +72,5 @@ object BuildPaths {
}

def apply(cwd: Path, buildLoader: BuildLoader, mode: BuildPaths.Mode): BuildPaths =
BuildPaths(cwd, buildLoader.bleepYaml, mode)
BuildPaths(cwd, buildLoader.bleepYaml, mode, buildLoader.existing.toOption.flatMap(_.wantedVersion.forceGet.toOption))
}
56 changes: 27 additions & 29 deletions bleep-core/src/scala/bleep/CoursierResolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import java.nio.file.{Files, Path}
trait CoursierResolver {
val params: CoursierResolver.Params

def resolve(deps: Set[Dependency], forceScalaVersion: Option[model.VersionScala]): Either[CoursierError, CoursierResolver.Result]
def resolve(deps: Set[model.Dep], versionCombo: model.VersionCombo): Either[CoursierError, CoursierResolver.Result]

final def force(deps: Set[model.Dep], versionCombo: model.VersionCombo, context: String): CoursierResolver.Result =
resolve(deps, versionCombo) match {
case Left(err) => throw new BleepException.ResolveError(err, context)
case Right(value) => value
}
}

object CoursierResolver {
Expand Down Expand Up @@ -54,6 +60,7 @@ object CoursierResolver {
)
}
}

def apply(
repos: List[model.Repository],
logger: Logger,
Expand All @@ -65,7 +72,7 @@ object CoursierResolver {
val params = Params(downloadSources, authentications, repos)
val direct = new Direct(new CoursierLogger(logger), params)
val cached = new Cached(logger, direct, cacheIn)
new WithBleepVersion(cached, wantedBleepVersion)
new TemplatedVersions(cached, wantedBleepVersion)
}

final case class Authentications(configs: Map[URI, Authentication])
Expand Down Expand Up @@ -176,17 +183,17 @@ object CoursierResolver {

val fileCache = FileCache[Task]().withLogger(logger)

override def resolve(deps: Set[Dependency], forceScalaVersion: Option[model.VersionScala]): Either[CoursierError, CoursierResolver.Result] = {
override def resolve(deps: Set[model.Dep], versionCombo: model.VersionCombo): Either[CoursierError, CoursierResolver.Result] = {
def go(remainingAttempts: Int): Either[CoursierError, Fetch.Result] = {
val newClassifiers = if (params.downloadSources) List(Classifier.sources) else Nil

Fetch[Task](fileCache)
.withRepositories(coursierRepos(params.repos, params.authentications))
.withDependencies(deps.toList.sortBy(_.toString()))
.withDependencies(deps.toList.sorted.map(_.asDependencyForce(None, versionCombo)))
.withResolutionParams(
ResolutionParams()
.withForceScalaVersion(forceScalaVersion.nonEmpty)
.withScalaVersionOpt(forceScalaVersion.map(_.scalaVersion))
.withForceScalaVersion(versionCombo.asScala.nonEmpty)
.withScalaVersionOpt(versionCombo.asScala.map(_.scalaVersion.scalaVersion))
)
.withMainArtifacts(true)
.addClassifiers(newClassifiers: _*)
Expand All @@ -207,10 +214,10 @@ object CoursierResolver {

private class Cached(logger: Logger, underlying: Direct, in: Path) extends CoursierResolver {
override val params = underlying.params
override def resolve(deps: Set[Dependency], forceScalaVersion: Option[model.VersionScala]): Either[CoursierError, CoursierResolver.Result] =
if (deps.exists(_.version.endsWith("-SNAPSHOT"))) underlying.resolve(deps, forceScalaVersion)
override def resolve(deps: Set[model.Dep], versionCombo: model.VersionCombo): Either[CoursierError, CoursierResolver.Result] =
if (deps.exists(_.version.endsWith("-SNAPSHOT"))) underlying.resolve(deps, versionCombo)
else {
val request = Cached.Request(underlying.fileCache.location, deps.toList.sortBy(_.toString()), underlying.params, forceScalaVersion.map(_.scalaVersion))
val request = Cached.Request(underlying.fileCache.location, deps.toList.sortBy(_.toString()), underlying.params, versionCombo)
val digest = request.asJson.noSpaces.hashCode // both `noSpaces` and `String.hashCode` should hopefully be stable
val cachePath = in / s"$digest.json"

Expand All @@ -229,10 +236,10 @@ object CoursierResolver {
cachedResult match {
case Some(value) => Right(value)
case None =>
val depNames = deps.map(_.module.name.value)
val depNames = deps.map(_.baseModuleName.value)
val ctxLogger = logger.withContext(cachePath).withContext(depNames)
ctxLogger.debug(s"coursier cache miss")
underlying.resolve(deps, forceScalaVersion).map {
underlying.resolve(deps, versionCombo).map {
case changingResult if changingResult.fullDetailedArtifacts.exists { case (_, _, artifact, _) => artifact.changing } =>
ctxLogger.info("Not caching because result is changing")
changingResult
Expand All @@ -245,16 +252,14 @@ object CoursierResolver {
}

private object Cached {
case class Request(cacheLocation: File, wanted: List[Dependency], params: Params, forceScalaVersion: Option[String])
case class Request(cacheLocation: File, wanted: List[model.Dep], params: Params, versionCombo: model.VersionCombo)

object Request {

import Result.codecDependency

implicit val codec: Codec[Request] =
Codec.forProduct4[Request, File, List[Dependency], Params, Option[String]]("cacheLocation", "wanted", "params", "forceScalaVersion")(
Request.apply
)(x => (x.cacheLocation, x.wanted, x.params, x.forceScalaVersion))
Codec.forProduct4[Request, File, List[model.Dep], Params, model.VersionCombo]("cacheLocation", "wanted", "params", "forceScalaVersion")(Request.apply)(
x => (x.cacheLocation, x.wanted, x.params, x.versionCombo)
)
}

case class Both(request: Request, result: Result)
Expand All @@ -263,19 +268,12 @@ object CoursierResolver {
}
}

class WithBleepVersion(outer: CoursierResolver, maybeWantedBleepVersion: Option[model.BleepVersion]) extends CoursierResolver {
class TemplatedVersions(outer: CoursierResolver, maybeWantedBleepVersion: Option[model.BleepVersion]) extends CoursierResolver {
override val params = outer.params
override def resolve(deps: Set[Dependency], forceScalaVersion: Option[model.VersionScala]): Either[CoursierError, Result] = {
val rewrittenDeps =
maybeWantedBleepVersion match {
case Some(wantedBleepVersion) =>
deps.map {
case dep if dep.version == constants.BleepVersionTemplate => dep.withVersion(wantedBleepVersion.value)
case dep => dep
}
case None => deps
}
outer.resolve(rewrittenDeps, forceScalaVersion)
override def resolve(deps: Set[model.Dep], versionCombo: model.VersionCombo): Either[CoursierError, Result] = {
val replacements = model.Replacements.versions(maybeWantedBleepVersion, versionCombo, includeEpoch = true, includeBinVersion = true)
val rewrittenDeps = deps.map(replacements.fill.dep)
outer.resolve(rewrittenDeps, versionCombo)
}
}
}
Loading

0 comments on commit 803fa9b

Please sign in to comment.