import sbt.Keys._ import sbt._ import sbt.internal.util.ManagedLogger import sbt.io.IO import sbt.librarymanagement.{ConfigurationFilter, DependencyFilter} import java.io.File object StdBits { /** Discovers dependencies of a project and copies them into the destination * directory. * * @param destination location where to put the dependencies * @param providedJarNames name of JARs generated by the local projects; * unexpected (old) files are removed, so this task * needs to know these files' names to avoid removing * them * @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, providedJarNames: Seq[String], ignoreScalaLibrary: Boolean ): 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 graalVmOrgs = if ("espresso".equals(System.getenv("ENSO_JAVA"))) { Seq() } else { GraalVM.modules.map(_.organization).distinct } // All graal related modules must be filtered away - they will be provided in // module-path, and so, they must not be included in std-bits polyglot directories. val graalModuleFilter = DependencyFilter.moduleFilter( organization = new SimpleFilter(orgName => { !graalVmOrgs.contains(orgName) }) ) val relevantFiles = libraryUpdates .select( configuration = configFilter, module = graalModuleFilter, 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(file => file.getName) ++ providedJarNames 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, destination, log) } for (file <- report.unmodified) { val dest = destination / file.getName if (!dest.exists()) { log.info(s"Adding missing std-bits dependency ${file.getName}.") updateDependency(file, destination, log) } } } } private def updateDependency( jar: File, destinationDir: File, logger: ManagedLogger ): Unit = { val destination = destinationDir / jar.getName IO.copyFile(jar, destination) } /** 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 ) = { 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") } } }