Skip to content

Commit

Permalink
wip: cross build to sbt 2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
eed3si9n committed Oct 5, 2024
1 parent cdf5a9b commit 8e7d2f0
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 89 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ jobs:
- name: Build and test
shell: bash
run: |
sbt -v clean scripted
sbt -v +compile scripted
51 changes: 15 additions & 36 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,36 @@ ThisBuild / version := {

ThisBuild / organization := "com.eed3si9n"

def scala212 = "2.12.18"
ThisBuild / crossScalaVersions := Seq(scala212)
ThisBuild / scalaVersion := scala212
def scala212 = "2.12.20"
def scala3 = "3.3.4"
ThisBuild / crossScalaVersions := Seq(scala212, scala3)
ThisBuild / scalaVersion := scala3

lazy val jarjar = "com.eed3si9n.jarjarabrams" %% "jarjar-abrams-core" % "1.14.0"

lazy val root = (project in file("."))
.enablePlugins(SbtPlugin, ContrabandPlugin, SbtPlugin)
.settings(pomConsistency2021DraftSettings)
.enablePlugins(SbtPlugin, ContrabandPlugin)
.settings(nocomma {
name := "sbt-assembly"
scalacOptions := Seq("-deprecation", "-unchecked", "-Dscalac.patmat.analysisBudget=1024", "-Xfuture")
libraryDependencies += jarjar
scalacOptions := Seq(
"-Xsource:3",
"-deprecation",
"-unchecked",
"-Dscalac.patmat.analysisBudget=1024",
"-Xfuture",
)
libraryDependencies += jarjar.cross(CrossVersion.for3Use2_13)
(pluginCrossBuild / sbtVersion) := {
scalaBinaryVersion.value match {
case "2.12" => "1.2.8"
case "2.12" => "1.5.8"
case _ => "2.0.0-M2"
}
}
Compile / generateContrabands / sourceManaged := baseDirectory.value / "src" / "main" / "scala"
scriptedLaunchOpts := { scriptedLaunchOpts.value ++
Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
}
scriptedBufferLog := false
scriptedSbt := "1.9.7"
})

ThisBuild / scmInfo := Some(
Expand All @@ -56,30 +62,3 @@ ThisBuild / publishTo := {
else Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
ThisBuild / publishMavenStyle := true

// See https://eed3si9n.com/pom-consistency-for-sbt-plugins
lazy val pomConsistency2021Draft = settingKey[Boolean]("experimental")

/**
* this is an unofficial experiment to re-publish plugins with better Maven compatibility
*/
def pomConsistency2021DraftSettings: Seq[Setting[_]] = Seq(
pomConsistency2021Draft := Set("true", "1")(sys.env.get("POM_CONSISTENCY").getOrElse("false")),
moduleName := {
if (pomConsistency2021Draft.value)
sbtPluginModuleName2021Draft(moduleName.value,
(pluginCrossBuild / sbtBinaryVersion).value)
else moduleName.value
},
projectID := {
if (pomConsistency2021Draft.value) sbtPluginExtra2021Draft(projectID.value)
else projectID.value
},
)

def sbtPluginModuleName2021Draft(n: String, sbtV: String): String =
s"""${n}_sbt${if (sbtV == "1.0") "1" else if (sbtV == "2.0") "2" else sbtV}"""

def sbtPluginExtra2021Draft(m: ModuleID): ModuleID =
m.withExtraAttributes(Map.empty)
.withCrossVersion(CrossVersion.binary)
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.9.9
sbt.version=1.10.2
14 changes: 14 additions & 0 deletions project/project/metals.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// format: off
// DO NOT EDIT! This file is auto-generated.

// This plugin enables semantic information to be produced by sbt.
// It also adds support for debugging using the Debug Adapter Protocol

addSbtPlugin("org.scalameta" % "sbt-metals" % "1.3.5")

// This plugin makes sure that the JDI tools are in the sbt classpath.
// JDI tools are used by the debug adapter server.

addSbtPlugin("org.scala-debugger" % "sbt-jdi-tools" % "1.1.1")

// format: on
36 changes: 36 additions & 0 deletions src/main/scala-2.12/PluginCompat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package sbtassembly

import java.nio.file.{ Path => NioPath }
import java.util.jar.{ Manifest => JManifest }
import sbt.*
import xsbti.FileConverter

private[sbtassembly] object PluginCompat {
type MainClass = sbt.Package.MainClass

object CollectionConverters

val moduleIDStr = Keys.moduleID.key
def parseModuleIDStrAttribute(m: ModuleID): ModuleID = m

def toNioPath(a: Attributed[File])(implicit conv: FileConverter): NioPath =
a.data.toPath()
def toFile(a: Attributed[File])(implicit conv: FileConverter): File =
a.data
def toNioPaths(cp: Seq[Attributed[File]])(implicit conv: FileConverter): Vector[NioPath] =
cp.map(_.data.toPath()).toVector
def toFiles(cp: Seq[Attributed[File]])(implicit conv: FileConverter): Vector[File] =
cp.map(_.data).toVector

type CacheKey = FilesInfo[ModifiedFileInfo] :+:
Map[String, (Boolean, String)] :+: // map of target paths that matched a merge strategy
JManifest :+:
// Assembly options...
Boolean :+:
Option[Seq[String]] :+:
Option[Int] :+:
Boolean :+:
HNil

val HListFormats = sbt.internal.util.HListFormats
}
26 changes: 26 additions & 0 deletions src/main/scala-3/PluginCompat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package sbtassembly

import java.io.File
import java.nio.file.{ Path => NioPath }
import sbt.*
import sbt.librarymanagement.ModuleID
import xsbti.{ FileConverter, HashedVirtualFileRef }

object PluginCompat:
type MainClass = PackageOption.MainClass

val CollectionConverters = scala.collection.parallel.CollectionConverters

val moduleIDStr = Keys.moduleIDStr
def parseModuleIDStrAttribute(str: String): ModuleID =
Classpaths.moduleIdJsonKeyFormat.read(str)

def toNioPath(a: Attributed[HashedVirtualFileRef])(implicit conv: FileConverter): NioPath =
conv.toPath(a.data)
inline def toFile(a: Attributed[HashedVirtualFileRef])(implicit conv: FileConverter): File =
toNioPath(a).toFile()
def toNioPaths(cp: Seq[Attributed[HashedVirtualFileRef]])(implicit conv: FileConverter): Vector[NioPath] =
cp.map(toNioPath).toVector
inline def toFiles(cp: Seq[Attributed[HashedVirtualFileRef]])(implicit conv: FileConverter): Vector[File] =
toNioPaths(cp).map(_.toFile())
end PluginCompat
9 changes: 0 additions & 9 deletions src/main/scala-sbt-1.0/PluginCompat.scala

This file was deleted.

71 changes: 36 additions & 35 deletions src/main/scala/sbtassembly/Assembly.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import com.eed3si9n.jarjarabrams._
import sbt.Def.Initialize
import sbt.Keys._
import sbt.Package.{ manifestFormat, JarManifest, MainClass, ManifestAttributes }
import sbt.internal.util.HListFormats._
import sbt.internal.util.HNil
import sbt.internal.util.Types.:+:
import sbt.internal.inc.classpath.ClasspathUtil
import sbt.io.{ DirectoryFilter => _, IO => _, Path => _, Using }
import sbt.util.FileInfo.lastModified
import sbt.util.Tracked.{ inputChanged, lastOutput }
Expand All @@ -15,20 +13,21 @@ import sbt.{ File, Logger, _ }
import sbt.Tags.Tag
import CacheImplicits._
import sbtassembly.AssemblyPlugin.autoImport.{ Assembly => _, _ }
import sbtassembly.PluginCompat.ClasspathUtilities

import java.io.{ BufferedInputStream, ByteArrayInputStream, FileInputStream, InputStream }
import java.net.URI
import java.nio.file.attribute.{ BasicFileAttributeView, FileTime, PosixFilePermission }
import java.nio.file.{ Path, _ }
import java.nio.file.{ Path => NioPath, _ }
import java.security.MessageDigest
import java.time.Instant
import java.util.jar.{ Attributes => JAttributes, JarFile, Manifest => JManifest }
import scala.annotation.tailrec
import scala.collection.GenSeq
import scala.collection.JavaConverters._
import scala.language.postfixOps
import scala.tools.nsc.io.Streamable
import xsbti.FileConverter
import PluginCompat.*
import CollectionConverters.{ given, * }

object Assembly {
// used for contraband
Expand All @@ -43,15 +42,6 @@ object Assembly {

val assemblyTag = Tag("assembly")

private[sbtassembly] type CacheKey = FilesInfo[ModifiedFileInfo] :+:
Map[String, (Boolean, String)] :+: // map of target paths that matched a merge strategy
JManifest :+:
// Assembly options...
Boolean :+:
Option[Seq[String]] :+:
Option[Int] :+:
Boolean :+:
HNil
private[sbtassembly] val scalaPre213Libraries = Vector(
"scala-actors",
"scala-compiler",
Expand All @@ -69,7 +59,7 @@ object Assembly {
private[sbtassembly] val jarFileSystemResource =
Using.resource((uri: URI) => FileSystems.newFileSystem(uri, Map("create" -> "true").asJava))
private[sbtassembly] val jarEntryInputStreamResource = Using.resource((entry: JarEntry) => entry.stream())
private[sbtassembly] val jarEntryOutputStreamResource = Using.resource((path: Path) =>
private[sbtassembly] val jarEntryOutputStreamResource = Using.resource((path: NioPath) =>
Files.newOutputStream(path, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)
)
private[sbtassembly] val byteArrayInputStreamResource =
Expand Down Expand Up @@ -185,13 +175,15 @@ object Assembly {
def assemblyTask(key: TaskKey[File]): Initialize[Task[File]] = Def.task {
val t = (key / test).value
val s = (key / streams).value
val conv = fileConverter.value
assemble(
(key / assemblyJarName).value.replaceAll(".jar", ""),
(key / assemblyOutputPath).value,
(assembly / fullClasspath).value,
(assembly / externalDependencyClasspath).value,
(key / assemblyOption).value,
(key / packageOptions).value,
conv,
s.cacheDirectory,
s.log
)
Expand Down Expand Up @@ -219,6 +211,7 @@ object Assembly {
externalDepClasspath: Classpath,
ao: AssemblyOption,
po: Seq[PackageOption],
conv: FileConverter,
cacheDir: File,
log: Logger
): File = {
Expand All @@ -230,16 +223,18 @@ object Assembly {
log.log(level, s"$desc end. Took ${end - start} ms")
res
}
implicit val ev: FileConverter = conv // used by PluginCompat
val (jars, dirs) = timed(Level.Debug, "Separate classpath projects and all dependencies") {
classpath.toVector.sortBy(_.data.getCanonicalPath).partition(c => ClasspathUtilities.isArchive(c.data))
classpath.toVector
.sortBy(_.data.toString)
.partition(x => ClasspathUtil.isArchive(toNioPath(x)))
}
val externalDeps = timed(Level.Debug, "Collect only external dependencies") {
externalDepClasspath.map(_.data).toSet.filter(ClasspathUtilities.isArchive)
externalDepClasspath.toSet
.filter(x => ClasspathUtil.isArchive(toNioPath(x)))
}
val excludedJars = timed(Level.Debug, "Collect excluded jar names") {
ao.excludedJars map {
_.data
}
val excludedJars: Vector[NioPath] = timed(Level.Debug, "Collect excluded jar names") {
toNioPaths(ao.excludedJars)
}
val scalaLibraries = {
val scalaVersionParts = VersionNumber(ao.scalaVersion)
Expand All @@ -250,26 +245,29 @@ object Assembly {

val filteredJars = timed(Level.Debug, "Filter jars") {
jars.flatMap {
case jar if excludedJars contains jar.data.asFile => None
case jar if isScalaLibraryFile(scalaLibraries, jar.data.asFile) =>
case jar if excludedJars.contains(jar) => None
case jar if isScalaLibraryFile(scalaLibraries, toNioPath(jar)) =>
if (ao.includeScala) Some(jar) else None
case jar if externalDeps contains jar.data.asFile =>
case jar if externalDeps.contains(jar) =>
if (ao.includeDependency) Some(jar) else None
case jar =>
if (ao.includeBin) Some(jar) else None
}
}

val classShader = shader(ao.shadeRules.filter(_.isApplicableToCompiling), log)
val classByParentDir =
if (!ao.includeBin) Vector.empty[(File, File)]
else dirs.flatMap(dir => (dir.data ** (-DirectoryFilter)).get.map(dir.data -> _))
val classByParentDir: Vector[(NioPath, NioPath)] =
if (!ao.includeBin) Vector.empty
else dirs.flatMap { dir0 =>
val dir = toNioPath(dir0)
(dir.toFile ** (-DirectoryFilter)).get().map(dir -> _.toPath())
}
val classMappings =
timed(Level.Debug, "Collect and shade project classes") {
classByParentDir
.flatMap { case (parentDir, file) =>
val originalTarget = file.relativeTo(parentDir).get.toPath.toString
classShader(originalTarget, () => new BufferedInputStream(new FileInputStream(file)))
val originalTarget = file.relativize(parentDir).toString
classShader(originalTarget, () => new BufferedInputStream(new FileInputStream(file.toFile())))
.map { case (shadedName, stream) =>
Project(targetJarName, originalTarget, shadedName, stream)
}
Expand All @@ -290,10 +288,11 @@ object Assembly {
val (jarFiles, jarFileEntries) = timed(Level.Debug, "Collect and shade dependency entries") {
filteredJars.par.map { jar =>
val module = jar.metadata
.get(moduleID.key)
.get(PluginCompat.moduleIDStr)
.map(PluginCompat.parseModuleIDStrAttribute)
.map(m => ModuleCoordinate(m.organization, m.name, m.revision))
.getOrElse(ModuleCoordinate("", jar.data.name.replaceAll(".jar", ""), ""))
val jarFile = new JarFile(jar.data)
val jarFile = new JarFile(toFile(jar))
jarFile -> jarFile
.entries()
.asScala
Expand Down Expand Up @@ -413,7 +412,7 @@ object Assembly {
}
) {
val (_, classes) = classByParentDir.unzip
val cacheKey = lastModified(classes.toSet ++ filteredJars.map(_.data).toSet) :+:
val cacheKey = lastModified(classes.map(_.toFile()).toSet ++ filteredJars.map(_.data).toSet) :+:
mergeStrategiesByPathList :+:
jarManifest :+:
ao.repeatableBuild :+:
Expand Down Expand Up @@ -488,8 +487,8 @@ object Assembly {
* @param file the given file to check
* @return flag representing if the file is a Scala library
*/
def isScalaLibraryFile(scalaLibraries: Vector[String], file: File): Boolean =
scalaLibraries exists { x => file.getName startsWith x }
def isScalaLibraryFile(scalaLibraries: Vector[String], file: NioPath): Boolean =
scalaLibraries exists { x => file.getFileName.toString.startsWith(x) }

private[sbtassembly] def shader(
shadeRules: SeqShadeRules,
Expand All @@ -505,6 +504,7 @@ object Assembly {
)
(name: String, inputStream: LazyInputStream) => {
val is = inputStream()
import scala.tools.nsc.io.Streamable
val shadeResult = bytecodeShader(Streamable.bytes(is), name)
if (shadeResult.isEmpty) log.debug(s"Shade discarded: $name")
shadeResult.map { case (bytes, shadedName) =>
Expand All @@ -521,6 +521,7 @@ object Assembly {
private[sbtassembly] def cachedAssembly(inputs: CacheKey, cacheDir: File, scalaVersion: String, log: Logger)(
buildAssembly: () => File
): File = {
import PluginCompat.HListFormats.*
val cacheBlock = inputChanged(cacheDir / s"assembly-cacheKey-$scalaVersion") { (inputChanged, _: CacheKey) =>
lastOutput(cacheDir / s"assembly-outputs-$scalaVersion") { (_: Unit, previousOutput: Option[File]) =>
val outputExists = previousOutput.exists(_.exists())
Expand Down
Loading

0 comments on commit 8e7d2f0

Please sign in to comment.