Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,7 @@ object Build {

/* Configuration of the org.scala-lang:scala-library:*.**.**-nonboostrapped project */
lazy val `scala-library-nonbootstrapped` = project.in(file("library"))
.enablePlugins(ScalaLibraryPlugin)
.settings(
name := "scala-library-nonbootstrapped",
moduleName := "scala-library",
Expand Down Expand Up @@ -1448,6 +1449,7 @@ object Build {

/* Configuration of the org.scala-lang:scala-library:*.**.**-boostrapped project */
lazy val `scala-library-bootstrapped` = project.in(file("library"))
.enablePlugins(ScalaLibraryPlugin)
.settings(
name := "scala-library-bootstrapped",
moduleName := "scala-library",
Expand All @@ -1474,8 +1476,11 @@ object Build {
publish / skip := true,
// Project specific target folder. sbt doesn't like having two projects using the same target folder
target := target.value / "scala-library-bootstrapped",
// we need to have the `scala-library` artifact as a dependency for `ScalaLibraryPlugin` to work
// this was the only way to not get the artifact evicted by sbt. Even a custom configuration didn't work
// NOTE: true is the default value, just making things clearer here
managedScalaInstance := true,
// Configure the nonbootstrapped compiler
managedScalaInstance := false,
scalaInstance := {
val externalLibraryDeps = (`scala3-library` / Compile / externalDependencyClasspath).value.map(_.data).toSet
val externalCompilerDeps = (`scala3-compiler` / Compile / externalDependencyClasspath).value.map(_.data).toSet
Expand All @@ -1494,7 +1499,7 @@ object Build {
val compilerJars = Seq(tastyCore, scala3Interfaces, scala3Compiler) ++ (externalCompilerDeps -- externalLibraryDeps)

Defaults.makeScalaInstance(
scalaVersion.value,
dottyNonBootstrappedVersion,
libraryJars = libraryJars,
allCompilerJars = compilerJars,
allDocJars = Seq.empty,
Expand Down
44 changes: 31 additions & 13 deletions project/ScalaLibraryPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import sbt.*
import sbt.Keys.*
import scala.jdk.CollectionConverters.*
import java.nio.file.Files
import xsbti.VirtualFileRef
import sbt.internal.inc.Stamper

object ScalaLibraryPlugin extends AutoPlugin {

override def trigger = noTrigger

val fetchScala2ClassFiles = taskKey[(Set[File], File)]("Fetch the files to use that were compiled with Scala 2")
//val scala2LibraryVersion = settingKey[String]("Version of the Scala 2 Standard Library")

override def projectSettings = Seq (
fetchScala2ClassFiles := {
Expand All @@ -37,17 +38,29 @@ object ScalaLibraryPlugin extends AutoPlugin {
} (Set(scalaLibraryBinaryJar)), target)

},
(Compile / compile) := {
(Compile / manipulateBytecode) := {
val stream = streams.value
val target = (Compile / classDirectory).value
val (files, reference) = fetchScala2ClassFiles.value;
val analysis = (Compile / compile).value
stream.log.info(s"Copying files from Scala 2 Standard Library to $target")
for (file <- files; id <- file.relativeTo(reference).map(_.toString())) {
if (filesToCopy(id)) {
stream.log.debug(s"Copying file '${id}' to ${target / id}")
IO.copyFile(file, target / id)
}
val previous = (Compile / manipulateBytecode).value
val analysis = previous.analysis match {
case analysis: sbt.internal.inc.Analysis => analysis
case _ => sys.error("Unexpected analysis type")
}

var stamps = analysis.stamps
for (file <- files;
id <- file.relativeTo(reference);
if filesToCopy(id.toString()); // Only Override Some Very Specific Files
dest = target / (id.toString);
ref <- dest.relativeTo((LocalRootProject / baseDirectory).value)
) {
// Copy the files to the classDirectory
IO.copyFile(file, dest)
// Update the timestamp in the analysis
stamps = stamps.markProduct(
VirtualFileRef.of(s"$${BASE}/$ref"),
Stamper.forFarmHashP(dest.toPath()))
Copy link
Member Author

@hamzaremmal hamzaremmal Jul 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used this Stamper (forFarmHashP) based on observation and trials. It would be nice to have a confirmation if this is something stable or should a more developed logic be adopted here to choose the stampers.

}

val overwrittenBinaries = Files.walk((Compile / classDirectory).value.toPath())
Expand All @@ -56,13 +69,18 @@ object ScalaLibraryPlugin extends AutoPlugin {
.map(_.toFile)
.map(_.relativeTo((Compile / classDirectory).value).get)
.toSet

val diff = files.filterNot(_.relativeTo(reference).exists(overwrittenBinaries))

IO.copy(diff.map { file =>
file -> (Compile / classDirectory).value / file.relativeTo(reference).get.getPath
})
// Copy all the specialized classes in the stdlib
// no need to update any stamps as these classes exist nowhere in the analysis
for (orig <- diff; dest <- orig.relativeTo(reference)) {
IO.copyFile(orig, ((Compile / classDirectory).value / dest.toString()))
}

analysis
previous
.withAnalysis(analysis.copy(stamps = stamps)) // update the analysis with the correct stamps
.withHasModified(true) // mark it as updated for sbt to update its caches
}
)

Expand Down
Loading