Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Do not merge] Adapt Zinc to use the new LibraryManagement API #335

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 7 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ lazy val zincRoot: Project = (project in file("."))
zincTesting,
zincPersist,
zincCore,
zincIvyIntegration,
zincLmIntegration,
zincCompile,
zincCompileCore,
compilerInterface,
Expand Down Expand Up @@ -169,7 +169,7 @@ lazy val zinc = (project in file("zinc"))
zincPersist,
zincCompileCore,
zincClassfile,
zincIvyIntegration % "compile->compile;test->test",
zincLmIntegration % "compile->compile;test->test",
zincTesting % Test
)
.configure(addBaseSettingsAndTestDeps)
Expand All @@ -187,7 +187,7 @@ lazy val zincTesting = (project in internalPath / "zinc-testing")
publishArtifact := false,
libraryDependencies ++= Seq(scalaCheck, scalatest, junit, sjsonnewScalaJson.value)
)
.configure(addSbtLm, addSbtUtilTesting)
.configure(addSbtUtilLogging, addSbtUtilTesting)

lazy val zincCompile = (project in file("zinc-compile"))
.dependsOn(zincCompileCore, zincCompileCore % "test->test")
Expand Down Expand Up @@ -241,14 +241,14 @@ lazy val zincBenchmarks = (project in internalPath / "zinc-benchmarks")
publishLocal := {}
)

lazy val zincIvyIntegration = (project in internalPath / "zinc-ivy-integration")
lazy val zincLmIntegration = (project in internalPath / "zinc-lm-integration")
.dependsOn(zincCompileCore, zincTesting % Test)
.settings(
baseSettings,
name := "zinc Ivy Integration",
name := "zinc Library Management Integration",
compileOrder := sbt.CompileOrder.ScalaThenJava
)
.configure(addSbtLm)
.configure(addSbtLmCore, addSbtLmIvyTest)

// sbt-side interface to compiler. Calls compiler-side interface reflectively
lazy val zincCompileCore = (project in internalPath / "zinc-compile-core")
Expand Down Expand Up @@ -374,7 +374,7 @@ lazy val zincClassfile = (project in internalPath / "zinc-classfile")
// re-implementation of scripted engine
lazy val zincScripted = (project in internalPath / "zinc-scripted")
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
.dependsOn(zinc, zincIvyIntegration % "test->test")
.dependsOn(zinc, zincLmIntegration % "test->test")
.settings(
minimalSettings,
name := "zinc Scripted",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@

import sbt.internal.inc.ZincComponentCompiler;
import sbt.internal.inc.ZincComponentManager;
import sbt.internal.librarymanagement.IvyConfiguration;
import sbt.librarymanagement.DependencyResolution;
import sbt.librarymanagement.Resolver;
import sbt.librarymanagement.ResolversSyntax;
import scala.None$;
import xsbti.ComponentProvider;
import xsbti.GlobalLock;
Expand All @@ -24,34 +23,14 @@ public interface ZincBridgeProvider {
* Returns an ivy resolver to resolve dependencies locally in the default `.ivy2/local`.
* <p>
* For those users interested in using Internet resolvers like Maven Central, you can
* instantiate them via {@link ResolversSyntax#DefaultMavenRepository()} et al.
* instantiate them via {@link Resolver#DefaultMavenRepository()} et al.
*
* @return A local ivy resolver.
*/
public static Resolver getLocalResolver() {
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.
Expand Down Expand Up @@ -86,16 +65,16 @@ public static ComponentProvider getDefaultComponentProvider(File componentsRoot)
* @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,
DependencyResolution dependencyResolution,
Logger logger) {
ZincComponentManager manager = new ZincComponentManager(lock, componentProvider, None$.empty(), logger);
return ZincComponentCompiler.interfaceProvider(manager, ivyConfiguration, scalaJarsTarget);
return ZincComponentCompiler.interfaceProvider(manager, dependencyResolution, scalaJarsTarget);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import java.net.URLClassLoader
import java.util.concurrent.Callable

import sbt.internal.inc.classpath.ClasspathUtilities
import sbt.io.{ Hash, IO }
import sbt.internal.librarymanagement._
import sbt.io.IO
import sbt.internal.librarymanagement.JsonUtil
import sbt.internal.util.FullLogger
import sbt.librarymanagement._
import sbt.librarymanagement.syntax._
Expand Down Expand Up @@ -58,7 +58,7 @@ private[sbt] object ZincComponentCompiler {
private class ZincCompilerBridgeProvider(
userProvidedBridgeSources: Option[ModuleID],
manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
dependencyResolution: DependencyResolution,
scalaJarsTarget: File
) extends CompilerBridgeProvider {

Expand All @@ -73,7 +73,8 @@ 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, dependencyResolution, bridgeSources, logger)
logger.debug(InterfaceUtil.f0(s"Getting $bridgeSources for Scala ${scalaInstance.version}"))
zinc.compiledBridgeJar
}
Expand All @@ -99,9 +100,15 @@ 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 moduleSettings = ModuleDescriptorConfiguration(wrapper, ModuleInfo(wrapper.name))
.withDependencies(dependencies)
.withConfigurations(ZincLMHelper.DefaultConfigurations)
val moduleDescriptor = dependencyResolution.moduleDescriptor(moduleSettings)
ZincLMHelper.update(dependencyResolution,
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."
Expand Down Expand Up @@ -137,17 +144,17 @@ private[sbt] object ZincComponentCompiler {
// Used by ZincUtil.
def interfaceProvider(compilerBridgeSource: ModuleID,
manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
dependencyResolution: DependencyResolution,
scalaJarsTarget: File): CompilerBridgeProvider =
new ZincCompilerBridgeProvider(Some(compilerBridgeSource),
manager,
ivyConfiguration,
dependencyResolution,
scalaJarsTarget)

def interfaceProvider(manager: ZincComponentManager,
ivyConfiguration: IvyConfiguration,
dependencyResolution: DependencyResolution,
scalaJarsTarget: File): CompilerBridgeProvider =
new ZincCompilerBridgeProvider(None, manager, ivyConfiguration, scalaJarsTarget)
new ZincCompilerBridgeProvider(None, manager, dependencyResolution, scalaJarsTarget)

private final val LocalIvy = s"$${user.home}/.ivy2/local/${Resolver.localBasePattern}"
final val LocalResolver: Resolver = {
Expand Down Expand Up @@ -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,
dependencyResolution: DependencyResolution,
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 = {
Expand Down Expand Up @@ -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.
Expand All @@ -268,12 +241,17 @@ private[inc] class ZincComponentCompiler(
*/
private def compileAndInstall(compilerBridgeId: String): Unit = {
import UnresolvedWarning.unresolvedWarningLines
val ivyModuleForBridge = wrapDependencyInModule(bridgeSources)
val moduleForBridge =
dependencyResolution.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(dependencyResolution,
moduleForBridge,
retrieveDirectory,
false,
buffered) match {
case Left(uw) =>
val mod = bridgeSources.toString
val unresolvedLines = unresolvedWarningLines.showLines(uw).mkString("\n")
Expand All @@ -294,70 +272,22 @@ 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(dependencyResolution: DependencyResolution,
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 {
dependencyResolution.update(module, updateConfiguration, warningConf, logger) match {
case Left(unresolvedWarning) =>
logger.debug(s"Couldn't retrieve module(s) ${prettyPrintDependency(module)}.")
Left(unresolvedWarning)
Expand All @@ -369,4 +299,28 @@ object ZincIvyActions {
Right(allFiles)
}
}

private def defaultUpdateConfiguration(targetDir: File, noSource: Boolean): UpdateConfiguration = {
val retrieve = RetrieveConfiguration()
.withRetrieveDirectory(targetDir)
.withOutputPattern(defaultRetrievePattern)
val logLevel = UpdateLogging.DownloadOnly
val defaultExcluded = Set("doc")
val finalExcluded = if (noSource) defaultExcluded + "src" else defaultExcluded
val artifactFilter = ArtifactTypeFilter.forbid(finalExcluded)
UpdateConfiguration()
.withRetrieveManaged(retrieve)
.withLogging(logLevel)
.withArtifactFilter(artifactFilter)
}

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(", ")
}

}
Loading