From f70442c5c790d42c808c78d6f29c6ca946188e44 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 13 May 2010 18:38:55 -0400 Subject: [PATCH] * new [ivy] section with 'cache-directory' label in launcher configuration to specify the cache directory used by the launcher for Scala and sbt * new label 'classifiers' accepted under [app] section to retrieve other artifacts for the application --- launch/BootConfiguration.scala | 12 +++++-- launch/ConfigurationParser.scala | 24 ++++++++++---- launch/Enumeration.scala | 5 +-- launch/Launch.scala | 23 +++++++------ launch/LaunchConfiguration.scala | 32 ++++++++++++------- launch/Update.scala | 23 +++++++------ .../main/resources/sbt/sbt.boot.properties | 8 +++-- launch/src/test/scala/ScalaProviderTest.scala | 2 +- 8 files changed, 85 insertions(+), 44 deletions(-) diff --git a/launch/BootConfiguration.scala b/launch/BootConfiguration.scala index c67b34f5e..5e95f54eb 100644 --- a/launch/BootConfiguration.scala +++ b/launch/BootConfiguration.scala @@ -52,10 +52,18 @@ private object BootConfiguration /** The Ivy pattern to use for retrieving the scala compiler and library. It is relative to the directory * containing all jars for the requested version of scala. */ val scalaRetrievePattern = ScalaDirectoryName + "/[artifact](-[classifier]).[ext]" - + + def artifactType(classifier: String) = + classifier match + { + case "sources" => "src" + case "javadoc" => "doc" + case _ => "jar" + } + /** The Ivy pattern to use for retrieving the application and its dependencies. It is relative to the directory * containing all jars for the requested version of scala. */ - def appRetrievePattern(appID: xsbti.ApplicationID) = appDirectoryName(appID, "/") + "(/[component])/[artifact]-[revision].[ext]" + def appRetrievePattern(appID: xsbti.ApplicationID) = appDirectoryName(appID, "/") + "(/[component])/[artifact]-[revision](-[classifier]).[ext]" /** The name of the directory to retrieve the application and its dependencies to.*/ def appDirectoryName(appID: xsbti.ApplicationID, sep: String) = appID.groupID + sep + appID.name + sep + appID.version diff --git a/launch/ConfigurationParser.scala b/launch/ConfigurationParser.scala index 31f69533c..2ee4fb55a 100644 --- a/launch/ConfigurationParser.scala +++ b/launch/ConfigurationParser.scala @@ -28,20 +28,22 @@ class ConfigurationParser extends NotNull def processSections(sections: SectionMap): LaunchConfiguration = { val ((scalaVersion, scalaClassifiers), m1) = processSection(sections, "scala", getScala) - val (app, m2) = processSection(m1, "app", getApplication) + val ((app, appClassifiers), m2) = processSection(m1, "app", getApplication) val (repositories, m3) = processSection(m2, "repositories", getRepositories) val (boot, m4) = processSection(m3, "boot", getBoot) val (logging, m5) = processSection(m4, "log", getLogging) val (properties, m6) = processSection(m5, "app-properties", getAppProperties) - check(m6, "section") - new LaunchConfiguration(scalaVersion, scalaClassifiers, app, repositories, boot, logging, properties) + val (cacheDir, m7) = processSection(m6, "ivy", getIvy) + check(m7, "section") + val classifiers = Classifiers("" :: scalaClassifiers, "" :: appClassifiers) + new LaunchConfiguration(scalaVersion, IvyOptions(cacheDir, classifiers, repositories), app, boot, logging, properties) } def getScala(m: LabelMap) = { val (scalaVersion, m1) = getVersion(m, "Scala version", "scala.version") val (scalaClassifiers, m2) = ids(m1, "classifiers", Nil) check(m2, "label") - (scalaVersion, "" :: scalaClassifiers) // the added "" ensures that the main jars are retrieved + (scalaVersion, scalaClassifiers) // the added "" ensures that the main jars are retrieved } def getVersion(m: LabelMap, label: String, defaultName: String): (Version, LabelMap) = process(m, "version", processVersion(label, defaultName)) def processVersion(label: String, defaultName: String)(value: Option[String]): Version = @@ -75,6 +77,12 @@ class ConfigurationParser extends NotNull def file(map: LabelMap, name: String, default: File): (File, LabelMap) = (orElse(getOrNone(map, name).map(toFile), default), map - name) + def getIvy(m: LabelMap): Option[File] = + { + val (cacheDir, m1) = file(m, "cache-directory", null) // fix this later + check(m1, "label") + if(cacheDir eq null) None else Some(cacheDir) + } def getBoot(m: LabelMap): BootSetup = { val (dir, m1) = file(m, "directory", toFile("project/boot")) @@ -96,7 +104,7 @@ class ConfigurationParser extends NotNull case (tpe :: paths, newM) => (Search(tpe, toFiles(paths)), newM) } - def getApplication(m: LabelMap): Application = + def getApplication(m: LabelMap): (Application, List[String]) = { val (org, m1) = id(m, "org", "org.scala-tools.sbt") val (name, m2) = id(m1, "name", "sbt") @@ -105,9 +113,11 @@ class ConfigurationParser extends NotNull val (components, m5) = ids(m4, "components", List("default")) val (crossVersioned, m6) = id(m5, "cross-versioned", "true") val (resources, m7) = ids(m6, "resources", Nil) - check(m7, "label") + val (classifiers, m8) = ids(m7, "classifiers", Nil) + check(m8, "label") val classpathExtra = toArray(toFiles(resources)) - new Application(org, name, rev, main, components, toBoolean(crossVersioned), classpathExtra) + val app = new Application(org, name, rev, main, components, toBoolean(crossVersioned), classpathExtra) + (app, classifiers) } def getRepositories(m: LabelMap): List[Repository] = { diff --git a/launch/Enumeration.scala b/launch/Enumeration.scala index 382581295..046227e03 100644 --- a/launch/Enumeration.scala +++ b/launch/Enumeration.scala @@ -22,8 +22,9 @@ class Enumeration extends NotNull Nil } } - def value(s: String) = new Value(s) - class Value(override val toString: String) extends NotNull + def value(s: String) = new Value(s, 0) + def value(s: String, i: Int) = new Value(s, i) + class Value(override val toString: String, val id: Int) extends NotNull def toValue(s: String): Value = elements.find(_.toString == s).getOrElse(error("Expected one of " + elements.mkString(",") + " (got: " + s + ")")) } \ No newline at end of file diff --git a/launch/Launch.scala b/launch/Launch.scala index 220b19607..828f9ca0a 100644 --- a/launch/Launch.scala +++ b/launch/Launch.scala @@ -32,7 +32,9 @@ object Launch } def initialized(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Unit = { + parsed.logging.debug("Parsed configuration: " + parsed) val resolved = ResolveVersions(parsed) + resolved.logging.debug("Resolved configuration: " + resolved) explicit(currentDirectory, resolved, arguments) } @@ -63,8 +65,9 @@ object Launch final class RunConfiguration(val scalaVersion: String, val app: xsbti.ApplicationID, val workingDirectory: File, val arguments: List[String]) extends NotNull import BootConfiguration.{appDirectoryName, baseDirectoryName, ScalaDirectoryName, TestLoadScalaClasses} -class Launch(val bootDirectory: File, repositories: List[Repository], scalaClassifiers: List[String]) extends xsbti.Launcher +class Launch private[xsbt](val bootDirectory: File, val ivyOptions: IvyOptions) extends xsbti.Launcher { + import ivyOptions.{cacheDirectory, classifiers, repositories} bootDirectory.mkdirs private val scalaProviders = new Cache[String, ScalaProvider](new ScalaProvider(_)) def getScala(version: String): xsbti.ScalaProvider = scalaProviders(version) @@ -79,7 +82,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository], scalaClass def launcher: xsbti.Launcher = Launch.this def parentLoader = topLoader - lazy val configuration = new UpdateConfiguration(bootDirectory, version, repositories) + lazy val configuration = new UpdateConfiguration(bootDirectory, cacheDirectory, version, repositories) lazy val libDirectory = new File(configuration.bootDirectory, baseDirectoryName(version)) lazy val scalaHome = new File(libDirectory, ScalaDirectoryName) def compilerJar = new File(scalaHome,CompilerModuleName + ".jar") @@ -87,7 +90,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository], scalaClass override def classpath = array(compilerJar, libraryJar) def baseDirectories = List(scalaHome) def testLoadClasses = TestLoadScalaClasses - def target = new UpdateScala(scalaClassifiers) + def target = new UpdateScala(classifiers.forScala) def failLabel = "Scala " + version def lockFile = updateLockFile def extraClasspath = array() @@ -102,7 +105,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository], scalaClass def parentLoader = ScalaProvider.this.loader def baseDirectories = appHome :: id.mainComponents.map(components.componentLocation).toList def testLoadClasses = List(id.mainClass) - def target = new UpdateApp(Application(id)) + def target = new UpdateApp(Application(id), classifiers.app) def failLabel = id.name + " " + id.version def lockFile = updateLockFile def mainClasspath = fullClasspath @@ -121,14 +124,16 @@ class Launch(val bootDirectory: File, repositories: List[Repository], scalaClass } object Launcher { - def apply(bootDirectory: File, repositories: List[Repository], scalaClassifiers: List[String]): xsbti.Launcher = - apply(bootDirectory, repositories, scalaClassifiers, GetLocks.find) - def apply(bootDirectory: File, repositories: List[Repository], scalaClassifiers: List[String], locks: xsbti.GlobalLock): xsbti.Launcher = - new Launch(bootDirectory, repositories, scalaClassifiers) { + def apply(bootDirectory: File, repositories: List[Repository]): xsbti.Launcher = + apply(bootDirectory, IvyOptions(None, Classifiers(Nil, Nil), repositories)) + def apply(bootDirectory: File, ivyOptions: IvyOptions): xsbti.Launcher = + apply(bootDirectory, ivyOptions, GetLocks.find) + def apply(bootDirectory: File, ivyOptions: IvyOptions, locks: xsbti.GlobalLock): xsbti.Launcher = + new Launch(bootDirectory, ivyOptions) { override def globalLock = locks } def apply(explicit: LaunchConfiguration): xsbti.Launcher = - new Launch(explicit.boot.directory, explicit.repositories, explicit.scalaClassifiers) + new Launch(explicit.boot.directory, explicit.ivyConfiguration) def defaultAppProvider(baseDirectory: File): xsbti.AppProvider = getAppProvider(baseDirectory, Configuration.configurationOnClasspath) def getAppProvider(baseDirectory: File, configLocation: URL): xsbti.AppProvider = { diff --git a/launch/LaunchConfiguration.scala b/launch/LaunchConfiguration.scala index f17a02965..e39d849b6 100644 --- a/launch/LaunchConfiguration.scala +++ b/launch/LaunchConfiguration.scala @@ -7,24 +7,26 @@ import Pre._ import java.io.File import java.net.URL -final case class LaunchConfiguration(scalaVersion: Version, scalaClassifiers: List[String], app: Application, repositories: List[Repository], boot: BootSetup, logging: Logging, appProperties: List[AppProperty]) extends NotNull +final case class LaunchConfiguration(scalaVersion: Version, ivyConfiguration: IvyOptions, app: Application, boot: BootSetup, logging: Logging, appProperties: List[AppProperty]) extends NotNull { def getScalaVersion = Version.get(scalaVersion) - def withScalaVersion(newScalaVersion: String) = LaunchConfiguration(new Version.Explicit(newScalaVersion), scalaClassifiers, app, repositories, boot, logging, appProperties) - def withApp(app: Application) = LaunchConfiguration(scalaVersion, scalaClassifiers, app, repositories, boot, logging, appProperties) - def withAppVersion(newAppVersion: String) = LaunchConfiguration(scalaVersion, scalaClassifiers, app.withVersion(new Version.Explicit(newAppVersion)), repositories, boot, logging, appProperties) - def withVersions(newScalaVersion: String, newAppVersion: String) = LaunchConfiguration(new Version.Explicit(newScalaVersion), scalaClassifiers, app.withVersion(new Version.Explicit(newAppVersion)), repositories, boot, logging, appProperties) - def map(f: File => File) = LaunchConfiguration(scalaVersion, scalaClassifiers, app.map(f), repositories, boot.map(f), logging, appProperties) + def withScalaVersion(newScalaVersion: String) = LaunchConfiguration(new Version.Explicit(newScalaVersion), ivyConfiguration, app, boot, logging, appProperties) + def withApp(app: Application) = LaunchConfiguration(scalaVersion, ivyConfiguration, app, boot, logging, appProperties) + def withAppVersion(newAppVersion: String) = LaunchConfiguration(scalaVersion, ivyConfiguration, app.withVersion(new Version.Explicit(newAppVersion)), boot, logging, appProperties) + def withVersions(newScalaVersion: String, newAppVersion: String) = LaunchConfiguration(new Version.Explicit(newScalaVersion), ivyConfiguration, app.withVersion(new Version.Explicit(newAppVersion)), boot, logging, appProperties) + def map(f: File => File) = LaunchConfiguration(scalaVersion, ivyConfiguration, app.map(f), boot.map(f), logging, appProperties) } - +final case class IvyOptions(cacheDirectory: Option[File], classifiers: Classifiers, repositories: List[Repository]) extends NotNull +final case class Classifiers(forScala: List[String], app: List[String]) extends NotNull sealed trait Version extends NotNull object Version { - final class Explicit(val value: String) extends Version + final class Explicit(val value: String) extends Version { override def toString = value } final class Implicit(val name: String, val default: Option[String]) extends Version { require(isNonEmpty(name), "Name cannot be empty") require(default.isEmpty || isNonEmpty(default.get), "Default cannot be the empty string") + override def toString = name + (default match { case Some(d) => "[" + d + "]"; case None => "" }) } object Implicit @@ -96,12 +98,18 @@ final class SetProperty(val value: String) extends PropertyInit final class PromptProperty(val label: String, val default: Option[String]) extends PropertyInit final class Logging(level: LogLevel.Value) extends NotNull +{ + import LogLevel._ + def log(s: => String, at: Value) = if(level.id <= at.id) stream(at).println("[" + at + "] " + s) + def debug(s: => String) = log(s, Debug) + private def stream(at: Value) = if(at == Error) System.err else System.out +} object LogLevel extends Enumeration { - val Debug = value("debug") - val Info = value("info") - val Warn = value("warn") - val Error = value("error") + val Debug = value("debug", 0) + val Info = value("info", 1) + val Warn = value("warn", 2) + val Error = value("error", 3) def apply(s: String): Logging = new Logging(toValue(s)) } diff --git a/launch/Update.scala b/launch/Update.scala index 5e1be2b36..41aab12c7 100644 --- a/launch/Update.scala +++ b/launch/Update.scala @@ -26,16 +26,16 @@ import util.{DefaultMessageLogger, Message, MessageLoggerEngine} import BootConfiguration._ -sealed trait UpdateTarget extends NotNull { def tpe: String } +sealed trait UpdateTarget extends NotNull { def tpe: String; def classifiers: List[String] } final class UpdateScala(val classifiers: List[String]) extends UpdateTarget { def tpe = "scala" } -final class UpdateApp(val id: Application) extends UpdateTarget { def tpe = "app" } +final class UpdateApp(val id: Application, val classifiers: List[String]) extends UpdateTarget { def tpe = "app" } -final class UpdateConfiguration(val bootDirectory: File, val scalaVersion: String, val repositories: List[Repository]) extends NotNull +final class UpdateConfiguration(val bootDirectory: File, val ivyCacheDirectory: Option[File], val scalaVersion: String, val repositories: List[Repository]) extends NotNull /** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/ final class Update(config: UpdateConfiguration) { - import config.{bootDirectory, repositories, scalaVersion} + import config.{bootDirectory, ivyCacheDirectory, repositories, scalaVersion} bootDirectory.mkdirs private def logFile = new File(bootDirectory, UpdateLogName) @@ -103,7 +103,7 @@ final class Update(config: UpdateConfiguration) case u: UpdateApp => val app = u.id val resolvedName = if(app.crossVersioned) app.name + "_" + scalaVersion else app.name - addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", Nil) + addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", u.classifiers) excludeScala(moduleID) System.out.println("Getting " + app.groupID + " " + resolvedName + " " + app.getVersion + " ...") } @@ -132,7 +132,7 @@ final class Update(config: UpdateConfiguration) val extraMap = new java.util.HashMap[String,String] if(!classifier.isEmpty) extraMap.put("e:classifier", classifier) - val ivyArtifact = new DefaultDependencyArtifactDescriptor(dep, name, "jar", "jar", null, extraMap) + val ivyArtifact = new DefaultDependencyArtifactDescriptor(dep, name, artifactType(classifier), "jar", null, extraMap) for(conf <- dep.getModuleConfigurations) dep.addDependencyArtifact(conf, ivyArtifact) } @@ -197,7 +197,7 @@ final class Update(config: UpdateConfiguration) if(repositories.isEmpty) error("No repositories defined.") for(repo <- repositories if includeRepo(repo)) newDefault.add(toIvyRepository(settings, repo)) - onDefaultRepositoryCacheManager(settings)(configureCache) + configureCache(settings, ivyCacheDirectory) settings.addResolver(newDefault) settings.setDefaultResolver(newDefault.getName) } @@ -207,11 +207,16 @@ final class Update(config: UpdateConfiguration) private[this] val Snapshot = "-SNAPSHOT" private[this] val ChangingPattern = ".*" + Snapshot private[this] val ChangingMatcher = PatternMatcher.REGEXP - private def configureCache(manager: DefaultRepositoryCacheManager) + private def configureCache(settings: IvySettings, dir: Option[File]) { + val cacheDir = dir.getOrElse(settings.getDefaultRepositoryCacheBasedir()) + val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) manager.setUseOrigin(true) manager.setChangingMatcher(ChangingMatcher) manager.setChangingPattern(ChangingPattern) + settings.addRepositoryCacheManager(manager) + settings.setDefaultRepositoryCacheManager(manager) + dir.foreach(dir => settings.setDefaultResolutionCacheBasedir(dir.getAbsolutePath)) } private def toIvyRepository(settings: IvySettings, repo: Repository) = { @@ -280,7 +285,7 @@ final class Update(config: UpdateConfiguration) if(m.matches) { val base = List(1,2,3).map(m.group).mkString(".") - val pattern = "http://scala-tools.org/repo-snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision].[ext]" + val pattern = "http://scala-tools.org/repo-snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision](-[classifier]).[ext]" val resolver = new URLResolver resolver.setName("Scala Tools Snapshots") diff --git a/launch/src/main/resources/sbt/sbt.boot.properties b/launch/src/main/resources/sbt/sbt.boot.properties index b10cd73aa..106de90a0 100644 --- a/launch/src/main/resources/sbt/sbt.boot.properties +++ b/launch/src/main/resources/sbt/sbt.boot.properties @@ -1,6 +1,6 @@ [scala] version: 2.7.7 -# classifiers: sources + #classifiers: sources, javadoc [app] org: org.scala-tools.sbt @@ -9,6 +9,7 @@ class: sbt.xMain components: xsbti cross-versioned: true + #classifiers: sources, javadoc [repositories] local @@ -35,4 +36,7 @@ build.scala.versions: quick=set(2.7.7), new=prompt(Scala version)[2.7.7], fill=prompt(Scala version)[2.7.7] sbt.version: quick=set(0.7.3), new=prompt(sbt version)[0.7.3], fill=prompt(sbt version)[0.7.3] project.scratch: quick=set(true) - project.initialize: quick=set(true), new=set(true) \ No newline at end of file + project.initialize: quick=set(true), new=set(true) + +#[ivy] +# cache-directory: /home/user/.ivy2/cache2 \ No newline at end of file diff --git a/launch/src/test/scala/ScalaProviderTest.scala b/launch/src/test/scala/ScalaProviderTest.scala index d16b1c8bd..c15084aee 100644 --- a/launch/src/test/scala/ScalaProviderTest.scala +++ b/launch/src/test/scala/ScalaProviderTest.scala @@ -68,7 +68,7 @@ object LaunchTest def testRepositories = List(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined.apply) def withLauncher[T](f: xsbti.Launcher => T): T = FileUtilities.withTemporaryDirectory { bootDirectory => - f(Launcher(bootDirectory, testRepositories, Nil)) + f(Launcher(bootDirectory, testRepositories)) } def mapScalaVersion(versionNumber: String) = scalaVersionMap.find(_._2 == versionNumber).getOrElse {