Adapt Zinc to use the new LibraryManagement API

This commit is contained in:
Martin Duhem 2017-07-06 15:35:34 +02:00
parent 34d0dae6e2
commit 763effd337
3 changed files with 92 additions and 130 deletions

View File

@ -9,7 +9,7 @@ package xsbti.compile;
import sbt.internal.inc.ZincComponentCompiler;
import sbt.internal.inc.ZincComponentManager;
import sbt.internal.librarymanagement.IvyConfiguration;
import sbt.librarymanagement.LibraryManagement;
import sbt.librarymanagement.Resolver;
import sbt.librarymanagement.ResolversSyntax;
import scala.None$;
@ -32,26 +32,6 @@ public interface ZincBridgeProvider {
return ZincComponentCompiler.LocalResolver();
}
/**
* Get the default ivy configuration to retrieve compiler components.
* <p>
* This method is useful to invoke {@link ZincBridgeProvider#getProvider(File, GlobalLock, ComponentProvider, IvyConfiguration, Logger)}.
* <p>
* In order to know which arguments to pass, reading the
* <a href="http://ant.apache.org/ivy/history/latest-milestone/concept.html">ivy main concepts</a>
* may be useful.
*
* @param baseDirectory The base directory for ivy.
* @param ivyHome The home for ivy.
* @param resolvers The resolvers to be used (usually local and Maven).
* See {@link ZincBridgeProvider#getProvider(File, GlobalLock, ComponentProvider, IvyConfiguration, Logger)}
* and {@link ResolversSyntax}.
* @return A default ivy configuration ready for fetching Zinc compiler components.
*/
public static IvyConfiguration getDefaultConfiguration(File baseDirectory, File ivyHome, Resolver[] resolvers, Logger logger) {
return ZincComponentCompiler.getDefaultConfiguration(baseDirectory, ivyHome, resolvers, logger);
}
/**
* Returns a global lock that does nothing but calling the callable to synchronize
* across threads. The lock file is used to resolve and download dependencies via ivy.
@ -86,16 +66,16 @@ public interface ZincBridgeProvider {
* @param lock The lock file used internally by Ivy to synchronize the dependency resolution.
* @param componentProvider A provider capable of retrieving existing components or installing
* new ones. The component provider manages compiled bridge sources.
* @param ivyConfiguration The ivy configuration used internally by the provider.
* @param libraryManagement The library management module to use to retrieve the bridge.
* @param logger The logger.
* @return A compiler bridge provider capable of fetching scala jars and the compiler bridge.
*/
public static CompilerBridgeProvider getProvider(File scalaJarsTarget,
GlobalLock lock,
ComponentProvider componentProvider,
IvyConfiguration ivyConfiguration,
LibraryManagement libraryManagement,
Logger logger) {
ZincComponentManager manager = new ZincComponentManager(lock, componentProvider, None$.empty(), logger);
return ZincComponentCompiler.interfaceProvider(manager, ivyConfiguration, scalaJarsTarget);
return ZincComponentCompiler.interfaceProvider(manager, libraryManagement, scalaJarsTarget);
}
}

View File

@ -58,7 +58,7 @@ private[sbt] object ZincComponentCompiler {
private class ZincCompilerBridgeProvider(
userProvidedBridgeSources: Option[ModuleID],
manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
libraryManagement: LibraryManagement,
scalaJarsTarget: File
) extends CompilerBridgeProvider {
@ -73,7 +73,7 @@ private[sbt] object ZincComponentCompiler {
logger: xsbti.Logger): File = {
val autoClasspath = ClasspathOptionsUtil.auto
val raw = new RawCompiler(scalaInstance, autoClasspath, logger)
val zinc = new ZincComponentCompiler(raw, manager, ivyConfiguration, bridgeSources, logger)
val zinc = new ZincComponentCompiler(raw, manager, libraryManagement, bridgeSources, logger)
logger.debug(InterfaceUtil.f0(s"Getting $bridgeSources for Scala ${scalaInstance.version}"))
zinc.compiledBridgeJar
}
@ -99,9 +99,16 @@ private[sbt] object ZincComponentCompiler {
val scalaCompiler = ModuleID(ScalaOrganization, ScalaCompilerID, scalaVersion)
val dependencies = Vector(scalaLibrary, scalaCompiler).map(_.withConfigurations(CompileConf))
val wrapper = dummyModule.withConfigurations(CompileConf)
val ivySbt: IvySbt = new IvySbt(ivyConfiguration)
val ivyModule = ZincIvyActions.getModule(ivySbt, wrapper, dependencies)
ZincIvyActions.update(ivyModule, scalaJarsTarget, noSource = true, fullLogger) match {
val moduleDescriptor =
libraryManagement.moduleDescriptor(wrapper,
dependencies,
None,
ZincLMHelper.DefaultConfigurations)
ZincLMHelper.update(libraryManagement,
moduleDescriptor,
scalaJarsTarget,
noSource = true,
fullLogger) match {
case Left(uw) =>
val unresolvedLines = unresolvedWarningLines.showLines(uw).mkString("\n")
val unretrievedMessage = s"The Scala compiler and library could not be retrieved."
@ -137,17 +144,17 @@ private[sbt] object ZincComponentCompiler {
// Used by ZincUtil.
def interfaceProvider(compilerBridgeSource: ModuleID,
manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
libraryManagement: LibraryManagement,
scalaJarsTarget: File): CompilerBridgeProvider =
new ZincCompilerBridgeProvider(Some(compilerBridgeSource),
manager,
ivyConfiguration,
libraryManagement,
scalaJarsTarget)
def interfaceProvider(manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
libraryManagement: LibraryManagement,
scalaJarsTarget: File): CompilerBridgeProvider =
new ZincCompilerBridgeProvider(None, manager, ivyConfiguration, scalaJarsTarget)
new ZincCompilerBridgeProvider(None, manager, libraryManagement, scalaJarsTarget)
private final val LocalIvy = s"$${user.home}/.ivy2/local/${Resolver.localBasePattern}"
final val LocalResolver: Resolver = {
@ -182,43 +189,22 @@ private[sbt] object ZincComponentCompiler {
new DefaultComponentProvider(targetDir)
}
def getDefaultConfiguration(baseDirectory: File,
ivyHome: File,
resolvers0: Array[Resolver],
log: xsbti.Logger): IvyConfiguration = {
import sbt.io.syntax._
val resolvers = resolvers0.toVector
val chainResolver = ChainedResolver("zinc-chain", resolvers)
new InlineIvyConfiguration(
paths = IvyPaths(baseDirectory, Some(ivyHome)),
resolvers = resolvers,
otherResolvers = Vector.empty,
moduleConfigurations = Vector(ModuleConfiguration("*", chainResolver)),
lock = None,
checksums = Vector.empty,
managedChecksums = false,
resolutionCacheDir = Some(ivyHome / "resolution-cache"),
updateOptions = UpdateOptions(),
log = log
)
}
}
/**
* Component compiler which is able to to retrieve the compiler bridge sources
* `sourceModule` using Ivy.
* `sourceModule` using a `LibraryManagement` instance.
* The compiled classes are cached using the provided component manager according
* to the actualVersion field of the RawCompiler.
*/
private[inc] class ZincComponentCompiler(
compiler: RawCompiler,
manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
libraryManagement: LibraryManagement,
bridgeSources: ModuleID,
log: Logger
) {
import sbt.internal.util.{ BufferedLogger, FullLogger }
private final val ivySbt: IvySbt = new IvySbt(ivyConfiguration)
private final val buffered = new BufferedLogger(FullLogger(log))
def compiledBridgeJar: File = {
@ -247,19 +233,6 @@ private[inc] class ZincComponentCompiler(
s"$id$binSeparator${scalaVersion}__$javaClassVersion"
}
/**
* Returns an ivy module that will wrap and download a given `moduleID`.
*
* @param moduleID The `moduleID` that needs to be wrapped in a dummy module to be downloaded.
*/
private[inc] def wrapDependencyInModule(moduleID: ModuleID): IvySbt#Module = {
import ZincComponentCompiler.{ sbtOrgTemp, modulePrefixTemp }
val sha1 = Hash.toHex(Hash(moduleID.name))
val dummyID = ModuleID(sbtOrgTemp, s"$modulePrefixTemp$sha1", moduleID.revision)
.withConfigurations(moduleID.configurations)
ZincIvyActions.getModule(ivySbt, dummyID, Vector(moduleID))
}
/**
* Resolves the compiler bridge sources, compiles them and installs the sbt component
* in the local filesystem to make sure that it's reused the next time is required.
@ -268,12 +241,17 @@ private[inc] class ZincComponentCompiler(
*/
private def compileAndInstall(compilerBridgeId: String): Unit = {
import UnresolvedWarning.unresolvedWarningLines
val ivyModuleForBridge = wrapDependencyInModule(bridgeSources)
val moduleForBridge =
libraryManagement.wrapDependencyInModule(bridgeSources)
IO.withTemporaryDirectory { binaryDirectory =>
val target = new File(binaryDirectory, s"$compilerBridgeId.jar")
buffered bufferQuietly {
IO.withTemporaryDirectory { retrieveDirectory =>
ZincIvyActions.update(ivyModuleForBridge, retrieveDirectory, false, buffered) match {
ZincLMHelper.update(libraryManagement,
moduleForBridge,
retrieveDirectory,
false,
buffered) match {
case Left(uw) =>
val mod = bridgeSources.toString
val unresolvedLines = unresolvedWarningLines.showLines(uw).mkString("\n")
@ -294,70 +272,23 @@ private[inc] class ZincComponentCompiler(
}
object ZincIvyActions {
type IvyModule = IvySbt#Module
/** Define the default configurations for a Zinc module wrapper. */
private final val DefaultConfigurations: Vector[Configuration] =
Vector(Configurations.Component, Configurations.Compile)
/**
* Create a module from its dummy wrapper and its dependencies.
*
* @param wrapper The ModuleID wrapper that will download the dependencies.
* @param deps The dependencies to be downloaded.
* @return A fully-fledged ivy module to be used for [[IvyActions]].
*/
private[inc] def getModule(ivySbt: IvySbt,
wrapper: ModuleID,
deps: Vector[ModuleID]): IvyModule = {
val moduleInfo = ModuleInfo(wrapper.name)
val componentIvySettings = InlineConfiguration(
validate = false,
ivyScala = None,
module = wrapper,
moduleInfo = moduleInfo,
dependencies = deps
).withConfigurations(DefaultConfigurations)
new ivySbt.Module(componentIvySettings)
}
// The implementation of this method is linked to `wrapDependencyInModule`
private def prettyPrintDependency(module: IvyModule): String = {
module.moduleSettings match {
case ic: InlineConfiguration =>
// Pretty print the module as `ModuleIDExtra.toStringImpl` does.
ic.dependencies.map(m => s"${m.organization}:${m.name}:${m.revision}").mkString(", ")
case _ => sys.error("Fatal: configuration to download was not inline.")
}
}
private object ZincLMHelper {
private final val warningConf = UnresolvedWarningConfiguration()
private final val defaultRetrievePattern = Resolver.defaultRetrievePattern
private def defaultUpdateConfiguration(targetDir: File, noSource: Boolean): UpdateConfiguration = {
val retrieve = RetrieveConfiguration(targetDir, defaultRetrievePattern, false, None)
val logLevel = UpdateLogging.DownloadOnly
val defaultExcluded = Set("doc")
val finalExcluded = if (noSource) defaultExcluded + "src" else defaultExcluded
val artifactFilter = ArtifactTypeFilter.forbid(finalExcluded)
UpdateConfiguration(retrieve = Some(retrieve),
missingOk = false,
logging = logLevel,
artifactFilter = artifactFilter,
offline = false,
frozen = false)
}
private[inc] final val DefaultConfigurations: Vector[Configuration] =
Vector(Configurations.Component, Configurations.Compile)
private[inc] def update(module: IvyModule,
private[inc] def update(libraryManagement: LibraryManagement,
module: ModuleDescriptor,
retrieveDirectory: File,
noSource: Boolean = false,
logger: Logger): Either[UnresolvedWarning, Vector[File]] = {
import IvyActions.updateEither
val updateConfiguration = defaultUpdateConfiguration(retrieveDirectory, noSource)
val dependencies = prettyPrintDependency(module)
logger.info(s"Attempting to fetch $dependencies.")
val clockForCache = LogicalClock.unknown
updateEither(module, updateConfiguration, warningConf, clockForCache, None, logger) match {
libraryManagement.update(module, updateConfiguration, warningConf, logger) match {
case Left(unresolvedWarning) =>
logger.debug(s"Couldn't retrieve module(s) ${prettyPrintDependency(module)}.")
Left(unresolvedWarning)
@ -369,4 +300,33 @@ object ZincIvyActions {
Right(allFiles)
}
}
private def defaultUpdateConfiguration(targetDir: File, noSource: Boolean): UpdateConfiguration = {
val retrieve =
RetrieveConfiguration(Some(targetDir), Some(defaultRetrievePattern), Some(false), None)
val logLevel = UpdateLogging.DownloadOnly
val defaultExcluded = Set("doc")
val finalExcluded = if (noSource) defaultExcluded + "src" else defaultExcluded
val artifactFilter = ArtifactTypeFilter.forbid(finalExcluded)
UpdateConfiguration(
retrieveManaged = Some(retrieve),
missingOk = Some(false),
logging = Some(logLevel),
logicalClock = None,
metadataDirectory = None,
artifactFilter = Some(artifactFilter),
offline = Some(false),
frozen = Some(false)
)
}
private def prettyPrintDependency(module: ModuleDescriptor): String = {
module.directDependencies
.map { m =>
// Pretty print the module as `ModuleIDExtra.toStringImpl` does.
s"${m.organization}:${m.name}:${m.revision}"
}
.mkString(", ")
}
}

View File

@ -1,20 +1,20 @@
package sbt.internal.inc
import java.io.File
import java.util.concurrent.Callable
import sbt.io.IO
import sbt.io.syntax._
import sbt.librarymanagement.{
DefaultMavenRepository,
FileRepository,
Patterns,
ChainedResolver,
IvyLibraryManagement,
ModuleConfiguration,
Resolver,
UpdateOptions
}
import sbt.internal.librarymanagement.{ InlineIvyConfiguration, IvyPaths }
import sbt.util.Logger
import xsbti.compile.CompilerBridgeProvider
import xsbti.{ ComponentProvider, GlobalLock }
/**
* Base class for test suites that must be able to fetch and compile the compiler bridge.
@ -28,7 +28,7 @@ abstract class BridgeProviderSpecification extends UnitSpec {
val resolvers = Array(ZincComponentCompiler.LocalResolver, DefaultMavenRepository)
private val ivyConfiguration =
ZincComponentCompiler.getDefaultConfiguration(currentBase, currentTarget, resolvers, log)
getDefaultConfiguration(currentBase, currentTarget, resolvers, log)
def secondaryCacheDirectory: File = {
val target = file("target").getAbsoluteFile
@ -40,7 +40,8 @@ abstract class BridgeProviderSpecification extends UnitSpec {
val secondaryCache = Some(secondaryCacheDirectory)
val componentProvider = ZincComponentCompiler.getDefaultComponentProvider(targetDir)
val manager = new ZincComponentManager(lock, componentProvider, secondaryCache, log)
ZincComponentCompiler.interfaceProvider(manager, ivyConfiguration, currentManaged)
val libraryManagement = new IvyLibraryManagement(ivyConfiguration)
ZincComponentCompiler.interfaceProvider(manager, libraryManagement, currentManaged)
}
def getCompilerBridge(targetDir: File, log: Logger, scalaVersion: String): File = {
@ -58,4 +59,25 @@ abstract class BridgeProviderSpecification extends UnitSpec {
val provider = getZincProvider(targetDir, logger)
provider.fetchScalaInstance(scalaVersion, logger)
}
private def getDefaultConfiguration(baseDirectory: File,
ivyHome: File,
resolvers0: Array[Resolver],
log: xsbti.Logger): InlineIvyConfiguration = {
import sbt.io.syntax._
val resolvers = resolvers0.toVector
val chainResolver = ChainedResolver("zinc-chain", resolvers)
new InlineIvyConfiguration(
paths = IvyPaths(baseDirectory, Some(ivyHome)),
resolvers = resolvers,
otherResolvers = Vector.empty,
moduleConfigurations = Vector(ModuleConfiguration("*", chainResolver)),
lock = None,
checksums = Vector.empty,
managedChecksums = false,
resolutionCacheDir = Some(ivyHome / "resolution-cache"),
updateOptions = UpdateOptions(),
log = log
)
}
}