enso/project/StdBits.scala
Hubert Plociniczak 26ef0f6b00
Include only runtime dependencies in std-bits (#3445)
`provided` classifier is completely omitted in the update report so we
cannot filter on that during `.select`.
Instead, we only consider runtime configuration which lists all the
necessary dependencies needed to run things.

This is a follow up on #182093808. With this change frgaal compiler is no
longer packaged.
2022-05-12 10:44:46 +00:00

155 lines
5.6 KiB
Scala

import LibraryManifestGenerator.BundledLibrary
import sbt.Keys._
import sbt._
import sbt.internal.util.ManagedLogger
import sbt.io.IO
import sbt.librarymanagement.{ConfigurationFilter, DependencyFilter}
import java.io.File
import scala.sys.process.Process
object StdBits {
/** Discovers dependencies of a project and copies them into the destination
* directory.
*
* @param destination location where to put the dependencies
* @param baseJarName name of the base generated JAR (if any); unexpected
* (old) files are removed, so this task needs to know
* this file's name to avoid removing it
* @param ignoreScalaLibrary whether to ignore Scala dependencies that are
* added by default be SBT and are not relevant in
* pure-Java projects
*/
def copyDependencies(
destination: File,
baseJarName: Option[String],
ignoreScalaLibrary: Boolean,
unpackedDeps: Set[String] = Set()
): Def.Initialize[Task[Unit]] =
Def.task {
val libraryUpdates = (Compile / update).value
val log = streams.value.log
val baseFilter: NameFilter = new ExactFilter(Configurations.Runtime.name)
val validConfig =
if (ignoreScalaLibrary) baseFilter - new ExactFilter(Configurations.ScalaTool.name)
else baseFilter
val configFilter: ConfigurationFilter =
DependencyFilter.configurationFilter(name = validConfig)
val graalOrg = new ExactFilter("org.graalvm.sdk")
val relevantFiles =
libraryUpdates
.select(
configuration = configFilter,
module = DependencyFilter.moduleFilter(organization = -graalOrg),
artifact = DependencyFilter.artifactFilter()
)
val dependencyStore =
streams.value.cacheStoreFactory.make("std-bits-dependencies")
Tracked.diffInputs(dependencyStore, FileInfo.hash)(relevantFiles.toSet) {
report =>
val expectedFileNames =
report.checked.map(
getDestinationFileName(_, unpackedDeps)
) ++ baseJarName.toSeq
for (existing <- IO.listFiles(destination)) {
if (!expectedFileNames.contains(existing.getName)) {
log.info(
s"Removing outdated std-bits dependency ${existing.getName}."
)
IO.delete(existing)
}
}
for (changed <- report.modified -- report.removed) {
log.info(
s"Updating changed std-bits dependency ${changed.getName}."
)
updateDependency(changed, unpackedDeps, destination, log)
}
for (file <- report.unmodified) {
val dest = destination / getDestinationFileName(file, unpackedDeps)
if (!dest.exists()) {
log.info(s"Adding missing std-bits dependency ${file.getName}.")
updateDependency(file, unpackedDeps, destination, log)
}
}
}
}
private def shouldUnpack(jar: File, unpacked: Set[String]): Boolean = {
// Maven stores dependencies like this:
// .../repo1.maven.org/maven2/org/apache/xmlbeans/xmlbeans/5.0.1/xmlbeans-5.0.1.jar
// therefore, the parent of the parent of the jar file is the directory
// named with the un-versioned library name.
unpacked.contains(jar.getParentFile.getParentFile.getName)
}
private def updateDependency(
jar: File,
unpacked: Set[String],
destinationDir: File,
logger: ManagedLogger
): Unit = {
val destination = destinationDir / getDestinationFileName(jar, unpacked)
if (shouldUnpack(jar, unpacked)) {
destination.mkdirs()
val exitCode = Process(s"jar xf ${jar.getAbsolutePath}", destination).!
if (exitCode != 0) {
logger.err(s"Could not unpack a dependency jar: $jar.")
throw new RuntimeException(s"Could not unpack a dependency jar: $jar.")
}
} else {
IO.copyFile(jar, destination)
}
}
private def getDestinationFileName(
file: File,
unpacked: Set[String]
): String = {
if (shouldUnpack(file, unpacked)) {
val name = file.getName
name.stripSuffix(".jar")
} else file.getName
}
/**
* Builds a single standard library package `name`. Should only be used
* in tasks used in local development.
*
* @param name name of the package, see `stdBitsProjects` in build.sbt
* @param root top directory where distribution is being built
* @param cache used for persisting the cached information
* @param log logger used in the task
* @param defaultDevEnsoVersion default `dev` version
*/
def buildStdLibPackage(
name: String,
root: File,
cacheFactory: sbt.util.CacheStoreFactory,
log: sbt.Logger,
defaultDevEnsoVersion: String
) = Def.task {
log.info(s"Building standard library package for '$name'")
val prefix = "Standard"
val targetPkgRoot = root / "lib" / prefix / name / defaultDevEnsoVersion
val sourceDir = file(s"distribution/lib/$prefix/$name/$defaultDevEnsoVersion")
if (!sourceDir.exists) {
throw new RuntimeException("Invalid standard library package " + name)
}
val result = DistributionPackage.copyDirectoryIncremental(
source = file(s"distribution/lib/$prefix/$name/$defaultDevEnsoVersion"),
destination = targetPkgRoot,
cache = cacheFactory.sub("engine-libraries").make(s"$prefix.$name"),
)
if (result) {
log.info(s"Package '$name' has been updated")
} else {
log.info(s"No changes detected for '$name' package")
}
}
}