mirror of https://github.com/sbt/sbt.git
Setup interface project for testing
This commit is contained in:
parent
5644b936fe
commit
67e13ad887
|
|
@ -4,37 +4,11 @@ import xsbti.{AnalysisCallback, Logger}
|
|||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
/** A component manager provides access to the pieces of xsbt that are distributed as components.
|
||||
* There are two types of components. The first type is compiled subproject jars with their dependencies.
|
||||
* The second type is a subproject distributed as a source jar so that it can be compiled against a specific
|
||||
* version of Scala.
|
||||
*
|
||||
* The component manager provides services to install and retrieve components to the local repository.
|
||||
* This is used for source jars so that the compilation need not be repeated for other projects on the same
|
||||
* machine.
|
||||
*/
|
||||
trait ComponentManager extends NotNull
|
||||
{
|
||||
def directory(id: String): File =
|
||||
{
|
||||
error("TODO: implement")
|
||||
}
|
||||
def jars(id: String): Iterable[File] =
|
||||
{
|
||||
val dir = directory(id)
|
||||
if(dir.isDirectory)
|
||||
FileUtilities.jars(dir)
|
||||
else
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Interface to the Scala compiler. This class uses the Scala library and compiler obtained through the 'scalaLoader' class
|
||||
* loader. This class requires a ComponentManager in order to obtain the interface code to scalac and the analysis plugin. Because
|
||||
* these call Scala code for a different Scala version, they must be compiled for the version of Scala being used.
|
||||
* It is essential that the provided 'scalaVersion' be a 1:1 mapping to the actual version of Scala being used for compilation
|
||||
* (-SNAPSHOT is bad). Otherwise, binary compatibility issues will ensue!*/
|
||||
* (-SNAPSHOT is not acceptable). Otherwise, binary compatibility issues will ensue!*/
|
||||
class Compiler(scalaLoader: ClassLoader, val scalaVersion: String, private[xsbt] val manager: ComponentManager)
|
||||
{
|
||||
// this is the instance used to compile the analysis
|
||||
|
|
@ -46,8 +20,10 @@ class Compiler(scalaLoader: ClassLoader, val scalaVersion: String, private[xsbt]
|
|||
def apply(arguments: Seq[String])
|
||||
{
|
||||
// reflection is required for binary compatibility
|
||||
// The following inputs ensure there is a compile error if the class names change, but they should not actually be used
|
||||
import scala.tools.nsc.{CompilerCommand, FatalError, Global, Settings, reporters, util}
|
||||
// The following import ensures there is a compile error if the class name changes,
|
||||
// but it should not be otherwise directly referenced
|
||||
import scala.tools.nsc.Main
|
||||
|
||||
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaLoader)
|
||||
val main = mainClass.asInstanceOf[{def process(args: Array[String]): Unit }]
|
||||
main.process(arguments.toArray)
|
||||
|
|
@ -56,82 +32,17 @@ class Compiler(scalaLoader: ClassLoader, val scalaVersion: String, private[xsbt]
|
|||
/** Interface to the compiler that uses the dependency analysis plugin.*/
|
||||
object analysis
|
||||
{
|
||||
/** The compiled plugin jar. This will be passed to scalac as a compiler plugin.*/
|
||||
private lazy val analyzerJar =
|
||||
componentCompiler("analyzerPlugin").toList match
|
||||
{
|
||||
case x :: Nil => x
|
||||
case Nil => error("Analyzer plugin component not found")
|
||||
case xs => error("Analyzer plugin component must be a single jar (was: " + xs.mkString(", ") + ")")
|
||||
}
|
||||
/** The compiled interface jar. This is used to configure and call the compiler. It redirects logging and sets up
|
||||
* the dependency analysis plugin.*/
|
||||
private lazy val interfaceJars = componentCompiler("compilerInterface")
|
||||
/** The jar containing the compiled plugin and the compiler interface code. This will be passed to scalac as a compiler plugin
|
||||
* and used to load the class that actually interfaces with Global.*/
|
||||
private lazy val interfaceJar = componentCompiler(ComponentCompiler.compilerInterfaceID)
|
||||
def apply(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger)
|
||||
{
|
||||
val argsWithPlugin = ("-Xplugin:" + analyzerJar.getAbsolutePath) :: arguments.toList
|
||||
val interfaceLoader = new URLClassLoader(interfaceJars.toSeq.map(_.toURI.toURL).toArray, scalaLoader)
|
||||
val argsWithPlugin = ("-Xplugin:" + interfaceJar.getAbsolutePath) :: arguments.toList
|
||||
val interfaceLoader = new URLClassLoader(Array(interfaceJar.toURI.toURL), scalaLoader)
|
||||
val interface = Class.forName("xsbt.CompilerInterface", true, interfaceLoader).newInstance
|
||||
val runnable = interface.asInstanceOf[{ def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger): Unit }]
|
||||
runnable.run(argsWithPlugin.toArray, callback, maximumErrors, log) // safe to pass across the ClassLoader boundary because the types are defined in Java
|
||||
}
|
||||
def forceInitialization() {interfaceJars; analyzerJar}
|
||||
}
|
||||
}
|
||||
class ComponentCompiler(compiler: Compiler)
|
||||
{
|
||||
import compiler.{manager, scalaVersion}
|
||||
|
||||
val xsbtiID = "xsbti"
|
||||
lazy val xsbtiJars =
|
||||
{
|
||||
val js = manager.jars(xsbtiID)
|
||||
if(js.isEmpty)
|
||||
error("Could not find required xsbti component")
|
||||
else
|
||||
js
|
||||
}
|
||||
|
||||
import FileUtilities.{copy, createDirectory, zip, jars, unzip, withTemporaryDirectory}
|
||||
def apply(id: String): Iterable[File] =
|
||||
{
|
||||
val binID = id + "-bin_" + scalaVersion
|
||||
val binaryDirectory = manager.directory(binID)
|
||||
if(binaryDirectory.isDirectory)
|
||||
jars(binaryDirectory)
|
||||
else
|
||||
{
|
||||
createDirectory(binaryDirectory)
|
||||
val srcID = id + "-src"
|
||||
val srcDirectory = manager.directory(srcID)
|
||||
if(srcDirectory.isDirectory)
|
||||
{
|
||||
val targetJar = new File(binaryDirectory, id + ".jar")
|
||||
compileSources(srcDirectory, compiler, targetJar, id)
|
||||
Seq(targetJar)
|
||||
}
|
||||
else
|
||||
notFound(id)
|
||||
}
|
||||
}
|
||||
private def notFound(id: String) = error("Couldn't find xsbt source component " + id + " for Scala " + scalaVersion)
|
||||
private def compileSources(srcDirectory: File, compiler: Compiler, targetJar: File, id: String)
|
||||
{
|
||||
val sources = jars(srcDirectory)
|
||||
if(sources.isEmpty)
|
||||
notFound(id)
|
||||
else
|
||||
{
|
||||
withTemporaryDirectory { dir =>
|
||||
val extractedSources = (Set[File]() /: sources) { (extracted, sourceJar)=> extracted ++ unzip(sourceJar, dir) }
|
||||
val (sourceFiles, resources) = extractedSources.partition(_.getName.endsWith(".scala"))
|
||||
withTemporaryDirectory { outputDirectory =>
|
||||
val arguments = Seq("-d", outputDirectory.getAbsolutePath, "-cp", xsbtiJars.mkString(File.pathSeparator)) ++ sourceFiles.toSeq.map(_.getAbsolutePath)
|
||||
compiler.raw(arguments)
|
||||
copy(resources, outputDirectory, PathMapper.relativeTo(dir))
|
||||
zip(Seq(outputDirectory), targetJar, true, PathMapper.relativeTo(outputDirectory))
|
||||
}
|
||||
}
|
||||
}
|
||||
def forceInitialization() {interfaceJar }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
object ComponentCompiler
|
||||
{
|
||||
val xsbtiID = "xsbti"
|
||||
val compilerInterfaceID = "compilerInterface"
|
||||
}
|
||||
class ComponentCompiler(compiler: Compiler)
|
||||
{
|
||||
import compiler.{manager, scalaVersion}
|
||||
import ComponentCompiler._
|
||||
|
||||
lazy val xsbtiJars = manager.files(xsbtiID)
|
||||
|
||||
import FileUtilities.{copy, createDirectory, zip, jars, unzip, withTemporaryDirectory}
|
||||
def apply(id: String): File =
|
||||
{
|
||||
val binID = id + "-bin_" + scalaVersion
|
||||
try { manager.file(binID) }
|
||||
catch { case e: Exception => compileAndInstall(id, binID) }
|
||||
}
|
||||
private def compileAndInstall(id: String, binID: String): File =
|
||||
{
|
||||
val srcID = id + "-src_" + scalaVersion
|
||||
val binaryDirectory = manager.location(binID)
|
||||
createDirectory(binaryDirectory)
|
||||
val targetJar = new File(binaryDirectory, id + ".jar")
|
||||
compileSources(manager.files(srcID), compiler, targetJar, id)
|
||||
manager.cache(binID)
|
||||
targetJar
|
||||
}
|
||||
/** Extract sources from source jars, compile them with the xsbti interfaces on the classpath, and package the compiled classes and
|
||||
* any resources from the source jars into a final jar.*/
|
||||
private def compileSources(sourceJars: Iterable[File], compiler: Compiler, targetJar: File, id: String)
|
||||
{
|
||||
withTemporaryDirectory { dir =>
|
||||
val extractedSources = (Set[File]() /: sourceJars) { (extracted, sourceJar)=> extracted ++ unzip(sourceJar, dir) }
|
||||
val (sourceFiles, resources) = extractedSources.partition(_.getName.endsWith(".scala"))
|
||||
withTemporaryDirectory { outputDirectory =>
|
||||
val arguments = Seq("-d", outputDirectory.getAbsolutePath, "-cp", xsbtiJars.mkString(File.pathSeparator)) ++ sourceFiles.toSeq.map(_.getAbsolutePath)
|
||||
compiler.raw(arguments)
|
||||
copy(resources, outputDirectory, PathMapper.relativeTo(dir))
|
||||
zip(Seq(outputDirectory), targetJar, true, PathMapper.relativeTo(outputDirectory))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ class Analyzer(val global: Global) extends Plugin
|
|||
|
||||
import global._
|
||||
|
||||
val name = "sbt-analyzer"
|
||||
val name = "xsbt-analyze"
|
||||
val description = "A plugin to find all concrete instances of a given class and extract dependency information."
|
||||
val components = List[PluginComponent](Component)
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<plugin>
|
||||
<name>xsbt-analyze</name>
|
||||
<classname>xsbt.Analyzer</classname>
|
||||
</plugin>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
import org.specs.Specification
|
||||
|
||||
object CheckBasic extends Specification
|
||||
{
|
||||
"Compiling basic file should succeed" in {
|
||||
val name = new File("Basic.scala")
|
||||
WithFiles( name -> "package org.example { object Basic }" ){ files =>
|
||||
TestCompile(files){ loader => Class.forName("org.example.Basic", false, loader) }
|
||||
}
|
||||
}
|
||||
|
||||
"Analysis plugin" should {
|
||||
"send source begin and end" in {
|
||||
val name = new File("Basic.scala")
|
||||
WithFiles(name -> "object Basic" ) { files =>
|
||||
CallbackTest(files) { callback =>
|
||||
(callback.beganSources) must haveTheSameElementsAs(files)
|
||||
(callback.endedSources) must haveTheSameElementsAs(files)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"detect applications" in {
|
||||
val name = new File("Main.scala")
|
||||
WithFiles(name -> "object Main { def main(args: Array[String]) {} }" ) { files =>
|
||||
CallbackTest(files) { callback =>
|
||||
println(callback.applications)
|
||||
(callback.applications) must haveTheSameElementsAs(files.map(file => (file, "Main")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import xsbti.{Logger, TestCallback, TestLogger}
|
||||
import FileUtilities.{classLocationFile, withTemporaryDirectory, write}
|
||||
|
||||
object TestCompile
|
||||
{
|
||||
def apply[T](arguments: Seq[String], superclassNames: Seq[String])(f: (TestCallback, Logger) => T): T =
|
||||
{
|
||||
val pluginLocation = classLocationFile[Analyzer]
|
||||
assert(pluginLocation.exists)
|
||||
val path = pluginLocation.getAbsolutePath
|
||||
val pluginArg = if(pluginLocation.getName.endsWith(".jar")) List("-Xplugin:" + path) else List("-Xpluginsdir", path)
|
||||
val testCallback = new TestCallback(superclassNames.toArray)
|
||||
val i = new CompilerInterface
|
||||
val newArgs = "-Xplugin-require:xsbt-analyze" :: pluginArg ::: arguments.toList
|
||||
TestLogger { log =>
|
||||
i.run(newArgs.toArray, testCallback, 5, log)
|
||||
f(testCallback, log)
|
||||
}
|
||||
}
|
||||
def apply[T](sources: Seq[File])(f: ClassLoader => T): T =
|
||||
CallbackTest.apply(sources, Nil){ case (callback, outputDir, log) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) }
|
||||
}
|
||||
object CallbackTest
|
||||
{
|
||||
def apply[T](sources: Iterable[File])(f: TestCallback => T): T =
|
||||
apply(sources.toSeq, Nil){ case (callback, outputDir, log) => f(callback) }
|
||||
def apply[T](sources: Seq[File], superclassNames: Seq[String])(f: (TestCallback, File, Logger) => T): T =
|
||||
{
|
||||
withTemporaryDirectory { outputDir =>
|
||||
val newArgs = "-d" :: outputDir.getAbsolutePath :: sources.map(_.getAbsolutePath).toList
|
||||
TestCompile(newArgs, superclassNames) { case (callback, log) => f(callback, outputDir, log) }
|
||||
}
|
||||
}
|
||||
}
|
||||
object WithFiles
|
||||
{
|
||||
def apply[T](sources: (File, String)*)(f: Seq[File] => T): T =
|
||||
{
|
||||
withTemporaryDirectory { dir =>
|
||||
val sourceFiles =
|
||||
for((file, content) <- sources) yield
|
||||
{
|
||||
assert(!file.isAbsolute)
|
||||
val to = new File(dir, file.getPath)
|
||||
write(to, content)
|
||||
to
|
||||
}
|
||||
f(sourceFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2009 Mark Harrah
|
||||
*/
|
||||
package xsbti;
|
||||
|
||||
public interface Versions
|
||||
{
|
||||
public static final String Sbt = "0.7";
|
||||
public static final int Interface = 1;
|
||||
public static final int BootInterface = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package xsbti
|
||||
|
||||
object f0
|
||||
{
|
||||
def apply[T](s: => T) = new F0[T] { def apply = s }
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package xsbti
|
||||
|
||||
import java.io.File
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class TestCallback(val superclassNames: Array[String]) extends AnalysisCallback
|
||||
{
|
||||
val invalidSuperclasses = new ArrayBuffer[String]
|
||||
val beganSources = new ArrayBuffer[File]
|
||||
val endedSources = new ArrayBuffer[File]
|
||||
val foundSubclasses = new ArrayBuffer[(File, String, String, Boolean)]
|
||||
val sourceDependencies = new ArrayBuffer[(File, File)]
|
||||
val jarDependencies = new ArrayBuffer[(File, File)]
|
||||
val classDependencies = new ArrayBuffer[(File, File)]
|
||||
val products = new ArrayBuffer[(File, File)]
|
||||
val applications = new ArrayBuffer[(File, String)]
|
||||
|
||||
def superclassNotFound(superclassName: String) { invalidSuperclasses += superclassName }
|
||||
def beginSource(source: File) { beganSources += source }
|
||||
def foundSubclass(source: File, subclassName: String, superclassName: String, isModule: Boolean): Unit =
|
||||
foundSubclasses += ((source, subclassName, superclassName, isModule))
|
||||
def sourceDependency(dependsOn: File, source: File) { sourceDependencies += ((dependsOn, source)) }
|
||||
def jarDependency(jar: File, source: File) { jarDependencies += ((jar, source)) }
|
||||
def classDependency(clazz: File, source: File) { classDependencies += ((clazz, source)) }
|
||||
def generatedClass(source: File, module: File) { products += ((source, module)) }
|
||||
def endSource(source: File) { endedSources += source }
|
||||
def foundApplication(source: File, className: String) { applications += ((source, className)) }
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package xsbti
|
||||
|
||||
class TestLogger extends Logger
|
||||
{
|
||||
private val buffer = new scala.collection.mutable.ArrayBuffer[F0[Unit]]
|
||||
def info(msg: F0[String]) = buffer("[info] ", msg)
|
||||
def warn(msg: F0[String]) = buffer("[warn] ", msg)
|
||||
def debug(msg: F0[String]) = buffer("[debug] ", msg)
|
||||
def error(msg: F0[String]) = buffer("[error] ", msg)
|
||||
def verbose(msg: F0[String]) = buffer("[verbose] ", msg)
|
||||
def show() { buffer.foreach(_()) }
|
||||
def clear() { buffer.clear() }
|
||||
def trace(t: F0[Throwable]) { buffer += f0(t().printStackTrace) }
|
||||
private def buffer(s: String, msg: F0[String]) { buffer += f0(println(s + msg())) }
|
||||
}
|
||||
object TestLogger
|
||||
{
|
||||
def apply[T](f: Logger => T): T =
|
||||
{
|
||||
val log = new TestLogger
|
||||
try { f(log) }
|
||||
catch { case e: Exception => log.show(); throw e }
|
||||
finally { log.clear() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package xsbt
|
||||
|
||||
import java.io.File
|
||||
import xsbti.Versions
|
||||
|
||||
/** A component manager provides access to the pieces of xsbt that are distributed as components.
|
||||
* There are two types of components. The first type is compiled subproject jars with their dependencies.
|
||||
* The second type is a subproject distributed as a source jar so that it can be compiled against a specific
|
||||
* version of Scala.
|
||||
*
|
||||
* The component manager provides services to install and retrieve components to the local repository.
|
||||
* This is used for source jars so that the compilation need not be repeated for other projects on the same
|
||||
* machine.
|
||||
*/
|
||||
class ComponentManager(baseDirectory: File, log: IvyLogger) extends NotNull
|
||||
{
|
||||
def location(id: String): File = new File(baseDirectory, id)
|
||||
def directory(id: String): File =
|
||||
{
|
||||
val dir = location(id)
|
||||
if(!dir.exists)
|
||||
update(id)
|
||||
dir
|
||||
}
|
||||
private def contents(dir: File): Seq[File] =
|
||||
{
|
||||
val fs = dir.listFiles
|
||||
if(fs == null) Nil else fs
|
||||
}
|
||||
def files(id: String): Iterable[File] =
|
||||
{
|
||||
val fs = contents(directory(id))
|
||||
if(!fs.isEmpty) fs else error("Could not find required component '" + id + "'")
|
||||
}
|
||||
def file(id: String): File =
|
||||
files(id).toList match {
|
||||
case x :: Nil => x
|
||||
case xs => error("Expected single file for component '" + id + "', found: " + xs.mkString(", "))
|
||||
}
|
||||
|
||||
def update(id: String): Unit =
|
||||
IvyActions.basicRetrieveLocal(sbtModuleID("manager"), Seq(sbtModuleID(id)), location(id), log)
|
||||
def sbtModuleID(id: String) = ModuleID("org.scala-tools.sbt", id, Versions.Sbt)
|
||||
def cache(id: String): Unit = IvyActions.basicPublishLocal(sbtModuleID(id), Nil, files(id), log)
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ import plugins.repository.Resource
|
|||
import plugins.repository.url.URLResource
|
||||
|
||||
/** Subclasses the default Ivy file parser in order to provide access to protected methods.*/
|
||||
private object CustomXmlParser extends XmlModuleDescriptorParser with NotNull
|
||||
private[xsbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotNull
|
||||
{
|
||||
import XmlModuleDescriptorParser.Parser
|
||||
class CustomParser(settings: IvySettings) extends Parser(CustomXmlParser, settings) with NotNull
|
||||
|
|
|
|||
|
|
@ -18,6 +18,30 @@ final class UpdateConfiguration(val retrieveDirectory: File, val outputPattern:
|
|||
|
||||
object IvyActions
|
||||
{
|
||||
def basicPublishLocal(moduleID: ModuleID, dependencies: Iterable[ModuleID], artifactFiles: Iterable[File], log: IvyLogger)
|
||||
{
|
||||
val artifacts = artifactFiles.map(Artifact.defaultArtifact)
|
||||
val (ivy, local) = basicLocalIvy(log)
|
||||
val module = new ivy.Module(ModuleConfiguration(moduleID, dependencies, artifacts))
|
||||
val srcArtifactPatterns = artifactFiles.map(_.getAbsolutePath)
|
||||
publish(module, local.name, srcArtifactPatterns, None, None)
|
||||
}
|
||||
def basicRetrieveLocal(moduleID: ModuleID, dependencies: Iterable[ModuleID], to: File, log: IvyLogger)
|
||||
{
|
||||
val (ivy, local) = basicLocalIvy(log)
|
||||
val module = new ivy.Module(ModuleConfiguration(moduleID, dependencies, Nil))
|
||||
val up = new UpdateConfiguration(to, defaultOutputPattern, false, true)
|
||||
update(module, up)
|
||||
}
|
||||
def defaultOutputPattern = "[artifact]-[revision](-[classifier]).[ext]"
|
||||
private def basicLocalIvy(log: IvyLogger) =
|
||||
{
|
||||
val local = Resolver.defaultLocal
|
||||
val paths = new IvyPaths(new File("."), None)
|
||||
val conf = new IvyConfiguration(paths, Seq(local), log)
|
||||
(new IvySbt(conf), local)
|
||||
}
|
||||
|
||||
/** Clears the Ivy cache, as configured by 'config'. */
|
||||
def cleanCache(ivy: IvySbt) = ivy.withIvy { _.getSettings.getRepositoryCacheManagers.foreach(_.clean()) }
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ final class ModuleConfiguration(val module: ModuleID, val dependencies: Iterable
|
|||
}
|
||||
object ModuleConfiguration
|
||||
{
|
||||
def apply(module: ModuleID, dependencies: Iterable[ModuleID], artifacts: Iterable[Artifact]) =
|
||||
new ModuleConfiguration(module, dependencies, NodeSeq.Empty, Nil, None, None, artifacts, false)
|
||||
def configurations(explicitConfigurations: Iterable[Configuration], defaultConfiguration: Option[Configuration]) =
|
||||
if(explicitConfigurations.isEmpty)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -311,15 +311,22 @@ object Artifact
|
|||
def apply(name: String, url: URL): Artifact =Artifact(name, extract(url, defaultType), extract(url, defaultExtension), None, Nil, Some(url))
|
||||
val defaultExtension = "jar"
|
||||
val defaultType = "jar"
|
||||
private[this] def extract(url: URL, default: String) =
|
||||
private[this] def extract(url: URL, default: String): String = extract(url.toString, default)
|
||||
private[this] def extract(name: String, default: String): String =
|
||||
{
|
||||
val s = url.toString
|
||||
val i = s.lastIndexOf('.')
|
||||
val i = name.lastIndexOf('.')
|
||||
if(i >= 0)
|
||||
s.substring(i+1)
|
||||
name.substring(i+1)
|
||||
else
|
||||
default
|
||||
}
|
||||
def defaultArtifact(file: File) =
|
||||
{
|
||||
val name = file.getName
|
||||
val i = name.lastIndexOf('.')
|
||||
val base = if(i >= 0) name.substring(0, i) else name
|
||||
Artifact(name, extract(name, defaultType), extract(name, defaultExtension), None, Nil, Some(file.toURI.toURL))
|
||||
}
|
||||
}
|
||||
/*
|
||||
object Credentials
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ package xsbt
|
|||
|
||||
import org.apache.ivy.util.{Message, MessageLogger}
|
||||
|
||||
trait IvyLogger
|
||||
trait IvyLogger extends NotNull
|
||||
{
|
||||
def info(msg: => String)
|
||||
def debug(msg: => String)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ import sbt._
|
|||
|
||||
class XSbt(info: ProjectInfo) extends ParentProject(info)
|
||||
{
|
||||
def utilPath = path("util")
|
||||
def compilePath = path("compile")
|
||||
|
||||
val commonDeps = project("common", "Dependencies", new CommonDependencies(_))
|
||||
val interfaceSub = project("interface", "Interface", new InterfaceProject(_))
|
||||
|
||||
|
|
@ -13,19 +10,23 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
val ioSub = project(utilPath / "io", "IO", new Base(_), controlSub, commonDeps)
|
||||
val classpathSub = project(utilPath / "classpath", "Classpath", new Base(_))
|
||||
|
||||
val analysisPluginSub = project(compilePath / "plugin", "Analyzer Compiler Plugin", new Base(_), interfaceSub)
|
||||
val compilerInterfaceSub = project(compilePath / "interface", "Compiler Interface", new Base(_), interfaceSub)
|
||||
val compilerInterfaceSub = project(compilePath / "interface", "Compiler Interface", new CompilerInterfaceProject(_), interfaceSub)
|
||||
|
||||
val ivySub = project("ivy", "Ivy", new IvyProject(_))
|
||||
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub)
|
||||
val logSub = project(utilPath / "log", "Logging", new Base(_))
|
||||
|
||||
val taskSub = project("tasks", "Tasks", new TaskProject(_), controlSub, collectionSub, commonDeps)
|
||||
val cacheSub = project("cache", "Cache", new CacheProject(_), taskSub, ioSub)
|
||||
val compilerSub = project(compilePath, "Compile", new Base(_), interfaceSub, ivySub, analysisPluginSub, ioSub, compilerInterfaceSub)
|
||||
val compilerSub = project(compilePath, "Compile", new Base(_), interfaceSub, ivySub, ioSub, compilerInterfaceSub)
|
||||
|
||||
class CommonDependencies(info: ProjectInfo) extends ParentProject(info)
|
||||
def utilPath = path("util")
|
||||
def compilePath = path("compile")
|
||||
|
||||
class CommonDependencies(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
val sc = "org.scala-tools.testing" % "scalacheck" % "1.5" % "test->default"
|
||||
val sp = "org.scala-tools.testing" % "specs" % "1.5.0" % "test->default"
|
||||
val ju = "junit" % "junit" % "4.5" % "test->default" // required by specs to compile properly
|
||||
}
|
||||
|
||||
override def parallelExecution = true
|
||||
|
|
@ -44,7 +45,22 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
|||
}
|
||||
class InterfaceProject(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
override def sourceExtensions: NameFilter = "*.java"
|
||||
override def mainSources = descendents(mainSourceRoots, "*.java")
|
||||
override def compileOrder = CompileOrder.JavaThenScala
|
||||
}
|
||||
class CompilerInterfaceProject(info: ProjectInfo) extends Base(info) with SourceProject
|
||||
{
|
||||
// these set up the test so that the classes and resources are both in the output resource directory
|
||||
// the main output path is removed so that the plugin (xsbt.Analyzer) is found in the output resource directory so that
|
||||
// the tests can configure that directory as -Xpluginsdir (which requires the scalac-plugin.xml and the classes to be together)
|
||||
override def testCompileAction = super.testCompileAction dependsOn(packageForTest)
|
||||
override def mainResources = super.mainResources +++ "scalac-plugin.xml"
|
||||
override def testClasspath = (super.testClasspath --- super.mainCompilePath) +++ ioSub.testClasspath +++ testPackagePath
|
||||
def testPackagePath = outputPath / "test.jar"
|
||||
lazy val packageForTest = packageTask(mainClasses +++ mainResources, testPackagePath, packageOptions).dependsOn(compile)
|
||||
}
|
||||
}
|
||||
trait SourceProject extends BasicScalaProject
|
||||
{
|
||||
override def packagePaths = packageSourcePaths
|
||||
}
|
||||
|
|
@ -7,9 +7,12 @@ import OpenResource._
|
|||
import ErrorHandling.translate
|
||||
|
||||
import java.io.{File, FileInputStream, InputStream, OutputStream}
|
||||
import java.net.{URISyntaxException, URL}
|
||||
import java.nio.charset.Charset
|
||||
import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest}
|
||||
import java.util.zip.{GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream}
|
||||
import scala.collection.mutable.HashSet
|
||||
import scala.reflect.{Manifest => SManifest}
|
||||
|
||||
object FileUtilities
|
||||
{
|
||||
|
|
@ -22,6 +25,31 @@ object FileUtilities
|
|||
private val BufferSize = 8192
|
||||
private val Newline = System.getProperty("line.separator")
|
||||
|
||||
def classLocation(cl: Class[_]): URL =
|
||||
{
|
||||
val codeSource = cl.getProtectionDomain.getCodeSource
|
||||
if(codeSource == null) error("No class location for " + cl)
|
||||
else codeSource.getLocation
|
||||
}
|
||||
def classLocationFile(cl: Class[_]): File = toFile(classLocation(cl))
|
||||
def classLocation[T](implicit mf: SManifest[T]): URL = classLocation(mf.erasure)
|
||||
def classLocationFile[T](implicit mf: SManifest[T]): File = classLocationFile(mf.erasure)
|
||||
|
||||
def toFile(url: URL) =
|
||||
try { new File(url.toURI) }
|
||||
catch { case _: URISyntaxException => new File(url.getPath) }
|
||||
|
||||
|
||||
// "base.extension" -> (base, extension)
|
||||
def split(name: String): (String, String) =
|
||||
{
|
||||
val lastDot = name.lastIndexOf('.')
|
||||
if(lastDot >= 0)
|
||||
(name.substring(0, lastDot), name.substring(lastDot+1))
|
||||
else
|
||||
(name, "")
|
||||
}
|
||||
|
||||
def createDirectory(dir: File): Unit =
|
||||
translate("Could not create directory " + dir + ": ")
|
||||
{
|
||||
|
|
@ -294,4 +322,15 @@ object FileUtilities
|
|||
}
|
||||
}
|
||||
}
|
||||
def defaultCharset = Charset.forName("UTF-8")
|
||||
def write(toFile: File, content: String): Unit = write(toFile, content, defaultCharset)
|
||||
def write(toFile: File, content: String, charset: Charset): Unit = write(toFile, content, charset, false)
|
||||
def write(file: File, content: String, charset: Charset, append: Boolean)
|
||||
{
|
||||
if(charset.newEncoder.canEncode(content))
|
||||
fileWriter(charset, append)(file) { w => w.write(content); None }
|
||||
else
|
||||
error("String cannot be encoded by charset " + charset.name)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue