More cleanup of Scala version handling, including managedScalaInstance to control automatic 'scala-tool' configuration and dependencies.

This commit is contained in:
Mark Harrah 2012-12-09 20:40:41 -05:00
parent 912948f5c8
commit e3745540c9
16 changed files with 81 additions and 30 deletions

View File

@ -36,7 +36,7 @@ final class CompilerArguments(scalaInstance: xsbti.compile.ScalaInstance, cp: xs
val scalaHome = System.getProperty("scala.home")
assert((scalaHome eq null) || scalaHome.isEmpty, "'scala.home' should not be set (was " + scalaHome + ")")
}
def createBootClasspathFor(classpath: Seq[File]) = createBootClasspath(hasLibrary(classpath))
def createBootClasspathFor(classpath: Seq[File]) = createBootClasspath(hasLibrary(classpath) || cp.compiler || cp.extra)
/** Add the correct Scala library jar to the boot classpath if `addLibrary` is true.*/
def createBootClasspath(addLibrary: Boolean) =

View File

@ -168,12 +168,13 @@ final class IvySbt(val configuration: IvyConfiguration)
val md = PomModuleDescriptorParser.getInstance.parseDescriptor(settings, toURL(pc.file), pc.validate)
val dmd = IvySbt.toDefaultModuleDescriptor(md)
IvySbt.addConfigurations(dmd, Configurations.defaultInternal)
for( is <- pc.ivyScala) {
val confParser = new CustomXmlParser.CustomParser(settings, Some(Configurations.DefaultMavenConfiguration.name))
val defaultConf = Configurations.DefaultMavenConfiguration.name
for( is <- pc.ivyScala) if(pc.autoScalaTools) {
val confParser = new CustomXmlParser.CustomParser(settings, Some(defaultConf))
confParser.setMd(dmd)
addScalaToolDependencies(dmd, confParser, is)
}
(dmd, "compile")
(dmd, defaultConf)
}
/** Parses the Ivy file 'ivyFile' from the given `IvyFileConfiguration`.*/
private def configureIvyFile(ifc: IvyFileConfiguration) =
@ -183,7 +184,7 @@ final class IvySbt(val configuration: IvyConfiguration)
parser.setSource(toURL(ifc.file))
parser.parse()
val dmd = IvySbt.toDefaultModuleDescriptor(parser.getModuleDescriptor())
for( is <- ifc.ivyScala )
for( is <- ifc.ivyScala ) if(ifc.autoScalaTools)
addScalaToolDependencies(dmd, parser, is)
(dmd, parser.getDefaultConf)
}

View File

@ -66,11 +66,11 @@ sealed trait ModuleSettings
def ivyScala: Option[IvyScala]
def noScala: ModuleSettings
}
final case class IvyFileConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean) extends ModuleSettings
final case class IvyFileConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings
{
def noScala = copy(ivyScala = None)
}
final case class PomConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean) extends ModuleSettings
final case class PomConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings
{
def noScala = copy(ivyScala = None)
}
@ -107,12 +107,12 @@ object ModuleSettings
log.debug("Autodetecting dependencies.")
val defaultPOMFile = IvySbt.defaultPOM(baseDirectory)
if(defaultPOMFile.canRead)
new PomConfiguration(defaultPOMFile, ivyScala, validate)
new PomConfiguration(defaultPOMFile, ivyScala, validate, true)
else
{
val defaultIvy = IvySbt.defaultIvyFile(baseDirectory)
if(defaultIvy.canRead)
new IvyFileConfiguration(defaultIvy, ivyScala, validate)
new IvyFileConfiguration(defaultIvy, ivyScala, validate, true)
else
{
log.warn("No dependency configuration found, using defaults.")

View File

@ -347,7 +347,7 @@ object Configurations
def default: Seq[Configuration] = defaultMavenConfigurations
def defaultMavenConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test, Provided, Optional)
def defaultInternal: Seq[Configuration] = Seq(CompileInternal, RuntimeInternal, TestInternal)
def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom, ScalaTool)
def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom)
def names(cs: Seq[Configuration]) = cs.map(_.name)
lazy val RuntimeInternal = optionalInternal(Runtime)

View File

@ -72,6 +72,7 @@ object Defaults extends BuildCommon
cancelable :== false,
sourcesInBase :== true,
autoScalaLibrary :== true,
managedScalaInstance :== true,
onLoad <<= onLoad ?? idFun[State],
onUnload <<= (onUnload ?? idFun[State]),
onUnload := { s => try onUnload.value(s) finally IO.delete(taskTemporaryDirectory.value) },
@ -284,8 +285,20 @@ object Defaults extends BuildCommon
scalaInstanceFromUpdate
}
}
private[this] def noToolConfiguration(autoInstance: Boolean): String =
{
val pre = "Missing Scala tool configuration from the 'update' report. "
val post =
if(autoInstance)
"'scala-tool' is normally added automatically, so this may indicate a bug in sbt or you may be removing it from ivyConfigurations, for example."
else
"Explicitly define scalaInstance or scalaHome or include Scala dependencies in the 'scala-tool' configuration."
pre + post
}
def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = Def.task {
val toolReport = update.value.configuration(Configurations.ScalaTool.name) getOrElse error("Missing Scala tool configuration.")
val toolReport = update.value.configuration(Configurations.ScalaTool.name) getOrElse
error(noToolConfiguration(managedScalaInstance.value))
def files(id: String) =
for { m <- toolReport.modules if m.module.name == id;
(art, file) <- m.artifacts if art.`type` == Artifact.DefaultType }
@ -730,7 +743,7 @@ object Defaults extends BuildCommon
lazy val baseClasspaths: Seq[Setting[_]] = Classpaths.publishSettings ++ Classpaths.baseSettings
lazy val configSettings: Seq[Setting[_]] = Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++ Classpaths.compilerPluginConfig
lazy val compileSettings: Seq[Setting[_]] = configSettings ++ (mainRunMainTask +: mainRunTask +: addBaseSources)
lazy val compileSettings: Seq[Setting[_]] = configSettings ++ (mainRunMainTask +: mainRunTask +: addBaseSources) ++ Classpaths.addUnmanagedLibrary
lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks
lazy val itSettings: Seq[Setting[_]] = inConfig(IntegrationTest)(testSettings)
@ -846,11 +859,11 @@ object Classpaths
projectDependencies <<= projectDependenciesTask,
dependencyOverrides in GlobalScope :== Set.empty,
libraryDependencies in GlobalScope :== Nil,
libraryDependencies ++= autoLibraryDependency(autoScalaLibrary.value, sbtPlugin.value, scalaOrganization.value, scalaVersion.value),
libraryDependencies ++= autoLibraryDependency(autoScalaLibrary.value && !scalaHome.value.isDefined && managedScalaInstance.value, sbtPlugin.value, scalaOrganization.value, scalaVersion.value),
allDependencies := {
val base = projectDependencies.value ++ libraryDependencies.value
val pluginAdjust = if(sbtPlugin.value) sbtDependency.value.copy(configurations = Some(Provided.name)) +: base else base
if(scalaHome.value.isDefined || ivyScala.value.isEmpty)
if(scalaHome.value.isDefined || ivyScala.value.isEmpty || !managedScalaInstance.value)
pluginAdjust
else
ScalaArtifacts.toolDependencies(scalaOrganization.value, scalaVersion.value) ++ pluginAdjust
@ -879,6 +892,7 @@ object Classpaths
(confs ++ confs.map(internalConfigurationMap.value) ++ (if(autoCompilerPlugins.value) CompilerPlugin :: Nil else Nil)).distinct
},
ivyConfigurations ++= Configurations.auxiliary,
ivyConfigurations ++= { if(managedScalaInstance.value && !scalaHome.value.isDefined) Configurations.ScalaTool :: Nil else Nil },
moduleSettings <<= moduleSettings0,
makePomConfiguration := new MakePomConfiguration(artifactPath in makePom value, projectInfo.value, None, pomExtra.value, pomPostProcess.value, pomIncludeRepository.value, pomAllRepositories.value),
deliverLocalConfiguration := deliverConfig(crossTarget.value, status = if (isSnapshot.value) "integration" else "release", logging = ivyLoggingLevel.value ),
@ -927,10 +941,9 @@ object Classpaths
Credentials.register(creds, s.log)
new IvySbt(conf)
}
def moduleSettings0: Initialize[Task[ModuleSettings]] =
(projectID, allDependencies, dependencyOverrides, ivyXML, ivyConfigurations, defaultConfiguration, ivyScala, ivyValidate, projectInfo) map {
(pid, deps, over, ivyXML, confs, defaultConf, ivyS, validate, pinfo) => new InlineConfiguration(pid, pinfo, deps, over, ivyXML, confs, defaultConf, ivyS, validate)
}
def moduleSettings0: Initialize[Task[ModuleSettings]] = Def.task {
new InlineConfiguration(projectID.value, projectInfo.value, allDependencies.value, dependencyOverrides.value, ivyXML.value, ivyConfigurations.value, defaultConfiguration.value, ivyScala.value, ivyValidate.value)
}
def sbtClassifiersTasks = inTask(updateSbtClassifiers)(Seq(
transitiveClassifiers in GlobalScope in updateSbtClassifiers ~= ( _.filter(_ != DocClassifier) ),
@ -1201,6 +1214,16 @@ object Classpaths
modifyForPlugin(plugin, ModuleID(org, ScalaArtifacts.LibraryID, version)) :: Nil
else
Nil
def addUnmanagedLibrary: Seq[Setting[_]] = Seq(
unmanagedJars in Compile <++= unmanagedScalaLibrary
)
def unmanagedScalaLibrary: Initialize[Task[Seq[File]]] =
Def.taskDyn {
if(autoScalaLibrary.value && scalaHome.value.isDefined)
Def.task { scalaInstance.value.libraryJar :: Nil }
else
Def.task { Nil }
}
import DependencyFilter._
def managedJars(config: Configuration, jarTypes: Set[String], up: UpdateReport): Classpath =
@ -1339,12 +1362,9 @@ trait BuildExtra extends BuildCommon
}
}
def externalIvyFile(file: Initialize[File] = baseDirectory / "ivy.xml", iScala: Initialize[Option[IvyScala]] = ivyScala): Setting[Task[ModuleSettings]] =
external(file, iScala)( (f, is, v) => new IvyFileConfiguration(f, is, v) )
moduleSettings := new IvyFileConfiguration(file.value, iScala.value, ivyValidate.value, managedScalaInstance.value)
def externalPom(file: Initialize[File] = baseDirectory / "pom.xml", iScala: Initialize[Option[IvyScala]] = ivyScala): Setting[Task[ModuleSettings]] =
external(file, iScala)( (f, is, v) => new PomConfiguration(f, is, v) )
private[this] def external(file: Initialize[File], iScala: Initialize[Option[IvyScala]])(make: (File, Option[IvyScala], Boolean) => ModuleSettings): Setting[Task[ModuleSettings]] =
moduleSettings <<= ((file zip iScala) zipWith ivyValidate) { case ((f, is), v) => task { make(f, is, v) } }
moduleSettings := new PomConfiguration(file.value, ivyScala.value, ivyValidate.value, managedScalaInstance.value)
def runInputTask(config: Configuration, mainClass: String, baseArguments: String*): Initialize[InputTask[Unit]] =
inputTask { result =>

View File

@ -309,6 +309,7 @@ object Keys
val classifiersModule = TaskKey[GetClassifiersModule]("classifiers-module", rank = CTask)
val conflictWarning = SettingKey[ConflictWarning]("conflict-warning", "Configures warnings for conflicts in dependency management.", CSetting)
val autoScalaLibrary = SettingKey[Boolean]("auto-scala-library", "Adds a dependency on scala-library if true.", ASetting)
val managedScalaInstance = SettingKey[Boolean]("managed-scala-instance", "Automatically obtains Scala tools as managed dependencies if true.", BSetting)
val sbtResolver = SettingKey[Resolver]("sbt-resolver", "Provides a resolver for obtaining sbt as a dependency.", BMinusSetting)
val sbtDependency = SettingKey[ModuleID]("sbt-dependency", "Provides a definition for declaring the current version of sbt.", BMinusSetting)
val sbtVersion = SettingKey[String]("sbt-version", "Provides the version of sbt. This setting should be not be modified.", AMinusSetting)

View File

@ -0,0 +1 @@
managedScalaInstance := false

View File

@ -1,6 +1,6 @@
seq(externalIvySettings(), externalIvyFile())
TaskKey[Unit]("check") <<= (baseDirectory, update) map { (base, report) =>
val files = report.matching( moduleFilter(organization = "org.scalacheck", name = "scalacheck*", revision = "1.9") )
TaskKey[Unit]("check") := {
val files = update.value.matching( moduleFilter(organization = "org.scalacheck", name = "scalacheck*", revision = "1.9") )
assert(!files.isEmpty, "ScalaCheck module not found in update report")
}

View File

@ -1,8 +1,11 @@
> update
# works because scalaVersion is the same as sbtScalaVersion
> compile
$ delete auto-instance.sbt
$ copy-file changes/scalacheck-ivy.xml ivy.xml
-> update
$ copy-file changes/scala-tools-ivysettings.xml ivysettings.xml
> check
> test:compile
> test:compile

View File

@ -7,6 +7,7 @@ object Test extends Build
libraryDependencies += "net.liftweb" % "lift-webkit" % "1.0" intransitive(),
libraryDependencies += "org.scalacheck" % "scalacheck" % "1.5" intransitive(),
autoScalaLibrary := false,
managedScalaInstance := false,
transitiveClassifiers := Seq("sources"),
TaskKey[Unit]("check-sources") <<= updateClassifiers map checkSources,
TaskKey[Unit]("check-binaries") <<= update map checkBinaries

View File

@ -0,0 +1,3 @@
public class A {
public static final int x = 3;
}

View File

@ -0,0 +1,5 @@
managedScalaInstance := false
externalResolvers := Nil
initialCommands := "println(A.x)"

View File

@ -0,0 +1,3 @@
scalaInstance := ScalaInstance(scalaVersion.value, appConfiguration.value.provider.scalaProvider)
scalaVersion := "invalid"

View File

@ -0,0 +1,14 @@
# test that a pure Java project can be compiled without a dependency on Scala library
> compile
# It can use the Scala REPL for the version of Scala that sbt runs with
> console
# A different version of Scala needs to be resolved, but we don't have any resolvers configured
> ++2.8.2
-> console
# With an explicit scalaInstance, the Scala tools configuration won't be resolved
$ copy-file changes/explicitInstance.sbt explicitInstance.sbt
> reload
> console

View File

@ -1,6 +1,5 @@
crossPaths := false
// the two spaces following this definition test that sbt handles extra whitespace on a line
name := "definition-lib-test"
version := "1.0"

View File

@ -2,13 +2,13 @@
> package
$ delete build.sbt
$ copy-file target/definition-lib-test-1.0.jar project/plugins/lib/test.jar
$ copy-file target/definition-lib-test-1.0.jar project/lib/test.jar
$ copy-file changes/build2.sbt build.sbt
# the copied project definition depends on the Test module in test.jar and will
# fail to compile if sbt did not put the jar in project/plugins/lib/ on the compile classpath
# fail to compile if sbt did not put the jar in project/lib/ on the compile classpath
> reload
# The project definition uses the class in test.jar and will fail here if sbt did not put the
# jar in project/plugins/lib on the runtime classpath
# jar in project/lib on the runtime classpath
> use-jar