From 9380ca02eb0fc97bbe3df27a74859f9ad3f62351 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 24 Mar 2015 16:12:51 -0400 Subject: [PATCH] Migrate to using the sbt/launcher module, rather than having the code embedded. * Remove launch/* code/tests, as these are in the sbt/launcher project. * Create a new project which will resolve launcher module from sonatype-snapshots, and repackage it for the currently building version of sbt. * Remove ComponentManagerTest which was relying DIRECTLY on launcher classes. We'll need to reconfigure this shortly to enable the tests again. Remaining TODOs - * Update resolvers so people can find the launcher. * Add ComponentManagerTest back. * Re-publish the sbt-launch.jar in the location it used to be published. --- build.sbt | 79 +--- ivy/src/test/scala/ComponentManagerTest.scala | 6 + launch/interface/NOTICE | 3 - .../src/main/java/xsbti/AppConfiguration.java | 10 - .../src/main/java/xsbti/AppMain.java | 25 - .../src/main/java/xsbti/AppProvider.java | 49 -- .../src/main/java/xsbti/ApplicationID.java | 58 --- .../main/java/xsbti/ComponentProvider.java | 53 --- .../src/main/java/xsbti/Continue.java | 5 - .../src/main/java/xsbti/CrossValue.java | 6 - .../interface/src/main/java/xsbti/Exit.java | 10 - .../src/main/java/xsbti/FullReload.java | 19 - .../src/main/java/xsbti/GlobalLock.java | 9 - .../src/main/java/xsbti/IvyRepository.java | 14 - .../src/main/java/xsbti/Launcher.java | 64 --- .../src/main/java/xsbti/MainResult.java | 12 - .../interface/src/main/java/xsbti/Manage.java | 6 - .../src/main/java/xsbti/MavenRepository.java | 9 - .../src/main/java/xsbti/Predefined.java | 29 -- .../main/java/xsbti/PredefinedRepository.java | 6 - .../interface/src/main/java/xsbti/Reboot.java | 16 - .../src/main/java/xsbti/Repository.java | 3 - .../main/java/xsbti/RetrieveException.java | 12 - .../src/main/java/xsbti/ScalaProvider.java | 29 -- .../interface/src/main/java/xsbti/Server.java | 36 -- .../src/main/java/xsbti/ServerMain.java | 17 - launch/src/main/scala/xsbt/boot/Boot.scala | 58 --- .../scala/xsbt/boot/BootConfiguration.scala | 122 ----- launch/src/main/scala/xsbt/boot/Cache.scala | 21 - .../src/main/scala/xsbt/boot/CheckProxy.scala | 39 -- .../main/scala/xsbt/boot/Configuration.scala | 161 ------- .../scala/xsbt/boot/ConfigurationParser.scala | 306 ------------- launch/src/main/scala/xsbt/boot/Create.scala | 49 -- .../main/scala/xsbt/boot/Enumeration.scala | 26 -- .../src/main/scala/xsbt/boot/Exceptions.scala | 9 - .../main/scala/xsbt/boot/FilteredLoader.scala | 39 -- launch/src/main/scala/xsbt/boot/Find.scala | 55 --- launch/src/main/scala/xsbt/boot/JAnsi.scala | 23 - launch/src/main/scala/xsbt/boot/Launch.scala | 398 ---------------- .../scala/xsbt/boot/LaunchConfiguration.scala | 154 ------- launch/src/main/scala/xsbt/boot/ListMap.scala | 37 -- launch/src/main/scala/xsbt/boot/Locks.scala | 101 ----- .../scala/xsbt/boot/ModuleDefinition.scala | 25 - .../scala/xsbt/boot/PlainApplication.scala | 48 -- launch/src/main/scala/xsbt/boot/Pre.scala | 104 ----- .../main/scala/xsbt/boot/ResolveValues.scala | 43 -- .../scala/xsbt/boot/ServerApplication.scala | 291 ------------ launch/src/main/scala/xsbt/boot/Update.scala | 426 ------------------ launch/src/main/scala/xsbt/boot/Using.scala | 40 -- .../src/main/scala/xsbt/boot/test/Apps.scala | 42 -- .../main/scala/xsbt/boot/test/Servers.scala | 72 --- project/Dependencies.scala | 1 + project/SbtLauncherPlugin.scala | 50 ++ 53 files changed, 79 insertions(+), 3246 deletions(-) delete mode 100644 launch/interface/NOTICE delete mode 100644 launch/interface/src/main/java/xsbti/AppConfiguration.java delete mode 100644 launch/interface/src/main/java/xsbti/AppMain.java delete mode 100644 launch/interface/src/main/java/xsbti/AppProvider.java delete mode 100644 launch/interface/src/main/java/xsbti/ApplicationID.java delete mode 100644 launch/interface/src/main/java/xsbti/ComponentProvider.java delete mode 100644 launch/interface/src/main/java/xsbti/Continue.java delete mode 100644 launch/interface/src/main/java/xsbti/CrossValue.java delete mode 100644 launch/interface/src/main/java/xsbti/Exit.java delete mode 100644 launch/interface/src/main/java/xsbti/FullReload.java delete mode 100644 launch/interface/src/main/java/xsbti/GlobalLock.java delete mode 100644 launch/interface/src/main/java/xsbti/IvyRepository.java delete mode 100644 launch/interface/src/main/java/xsbti/Launcher.java delete mode 100644 launch/interface/src/main/java/xsbti/MainResult.java delete mode 100644 launch/interface/src/main/java/xsbti/Manage.java delete mode 100644 launch/interface/src/main/java/xsbti/MavenRepository.java delete mode 100644 launch/interface/src/main/java/xsbti/Predefined.java delete mode 100644 launch/interface/src/main/java/xsbti/PredefinedRepository.java delete mode 100644 launch/interface/src/main/java/xsbti/Reboot.java delete mode 100644 launch/interface/src/main/java/xsbti/Repository.java delete mode 100644 launch/interface/src/main/java/xsbti/RetrieveException.java delete mode 100644 launch/interface/src/main/java/xsbti/ScalaProvider.java delete mode 100644 launch/interface/src/main/java/xsbti/Server.java delete mode 100644 launch/interface/src/main/java/xsbti/ServerMain.java delete mode 100644 launch/src/main/scala/xsbt/boot/Boot.scala delete mode 100644 launch/src/main/scala/xsbt/boot/BootConfiguration.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Cache.scala delete mode 100644 launch/src/main/scala/xsbt/boot/CheckProxy.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Configuration.scala delete mode 100644 launch/src/main/scala/xsbt/boot/ConfigurationParser.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Create.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Enumeration.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Exceptions.scala delete mode 100644 launch/src/main/scala/xsbt/boot/FilteredLoader.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Find.scala delete mode 100644 launch/src/main/scala/xsbt/boot/JAnsi.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Launch.scala delete mode 100644 launch/src/main/scala/xsbt/boot/LaunchConfiguration.scala delete mode 100644 launch/src/main/scala/xsbt/boot/ListMap.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Locks.scala delete mode 100644 launch/src/main/scala/xsbt/boot/ModuleDefinition.scala delete mode 100644 launch/src/main/scala/xsbt/boot/PlainApplication.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Pre.scala delete mode 100644 launch/src/main/scala/xsbt/boot/ResolveValues.scala delete mode 100644 launch/src/main/scala/xsbt/boot/ServerApplication.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Update.scala delete mode 100644 launch/src/main/scala/xsbt/boot/Using.scala delete mode 100644 launch/test-sample/src/main/scala/xsbt/boot/test/Apps.scala delete mode 100644 launch/test-sample/src/main/scala/xsbt/boot/test/Servers.scala create mode 100644 project/SbtLauncherPlugin.scala diff --git a/build.sbt b/build.sbt index ff21fc8f9..5d963b424 100644 --- a/build.sbt +++ b/build.sbt @@ -46,12 +46,21 @@ lazy val root: Project = (project in file(".")). settings(minimalSettings ++ rootSettings: _*). settings( publish := {}, - publishLocal := { - val p = (proguard in (proguardedLauncherProj, Proguard)).value - IO.copyFile(p, target.value / p.getName) - } + publishLocal := {} ) +// This is used to configure an sbt-launcher for this version of sbt. +lazy val bundledLauncherProj = + (project in file("launch")). + settings(minimalSettings:_*). + settings(inConfig(Compile)(Transform.configSettings):_*). + enablePlugins(SbtLauncherPlugin). + settings( + publish := {}, + publishLocal := {} + ) + + // This is used only for command aggregation lazy val allPrecompiled: Project = (project in file("all-precompiled")). aggregate(precompiled282, precompiled292, precompiled293). @@ -63,49 +72,6 @@ lazy val allPrecompiled: Project = (project in file("all-precompiled")). /* ** subproject declarations ** */ - -// the launcher. Retrieves, loads, and runs applications based on a configuration file. -lazy val launchProj = (project in launchPath). - dependsOn(ioProj % "test->test", interfaceProj % Test). - settings(testedBaseSettings: _*). - settings( - name := "Launcher", - libraryDependencies ++= Seq(ivy, Dependencies.launcherInterface), - compile in Test <<= compile in Test dependsOn (publishLocal in interfaceProj, publishLocal in testSamples) - ). - settings(inConfig(Compile)(Transform.configSettings): _*). - settings(inConfig(Compile)(Transform.transSourceSettings ++ Seq( - Transform.inputSourceDirectory <<= (sourceDirectory in crossProj) / "input_sources", - Transform.sourceProperties := Map("cross.package0" -> "xsbt", "cross.package1" -> "boot") - )): _*) - -// the proguarded launcher -// the launcher is published with metadata so that the scripted plugin can pull it in -// being proguarded, it shouldn't ever be on a classpath with other jars, however -lazy val proguardedLauncherProj = (project in file("sbt-launch")). - configs(Proguard). - settings(minimalSettings ++ LaunchProguard.settings ++ LaunchProguard.specific(launchProj) ++ - Release.launcherSettings(proguard in Proguard): _*). - settings( - name := "sbt-launch", - moduleName := "sbt-launch", - description := "sbt application launcher", - publishArtifact in packageSrc := false, - autoScalaLibrary := false, - publish <<= Seq(publish, Release.deployLauncher).dependOn, - publishLauncher <<= Release.deployLauncher, - packageBin in Compile <<= proguard in Proguard - ) - -// used to test the retrieving and loading of an application: sample app is packaged and published to the local repository -lazy val testSamples = (project in launchPath / "test-sample"). - dependsOn(interfaceProj). - settings(baseSettings ++ noPublishSettings: _*). - settings( - name := "Launch Test", - libraryDependencies ++= Seq(scalaCompiler.value, Dependencies.launcherInterface) - ) - // defines Java structures used across Scala versions, such as the API structures and relationships extracted by // the analysis compiler phases and passed back to sbt. The API structures are defined in a simple // format from which Java sources are generated by the datatype generator Projproject @@ -252,7 +218,7 @@ lazy val logicProj = (project in utilPath / "logic"). // Apache Ivy integration lazy val ivyProj = (project in file("ivy")). - dependsOn(interfaceProj, crossProj, logProj % "compile;test->test", ioProj % "compile;test->test", launchProj % "test->test", collectionProj). + dependsOn(interfaceProj, crossProj, logProj % "compile;test->test", ioProj % "compile;test->test", /*launchProj % "test->test",*/ collectionProj). settings(baseSettings: _*). settings( name := "Ivy", @@ -321,7 +287,7 @@ lazy val runProj = (project in file("run")). // Compiler-side interface to compiler that is compiled against the compiler being used either in advance or on the fly. // Includes API and Analyzer phases that extract source API and relationships. lazy val compileInterfaceProj = (project in compilePath / "interface"). - dependsOn(interfaceProj % "compile;test->test", ioProj % "test->test", logProj % "test->test", launchProj % "test->test", apiProj % "test->test"). + dependsOn(interfaceProj % "compile;test->test", ioProj % "test->test", logProj % "test->test", /*launchProj % "test->test",*/ apiProj % "test->test"). settings(baseSettings ++ precompiledSettings: _*). settings( name := "Compiler Interface", @@ -361,7 +327,7 @@ lazy val compilePersistProj = (project in compilePath / "persist"). // sbt-side interface to compiler. Calls compiler-side interface reflectively lazy val compilerProj = (project in compilePath). dependsOn(interfaceProj % "compile;test->test", logProj, ioProj, classpathProj, apiProj, classfileProj, - logProj % "test->test", launchProj % "test->test"). + logProj % "test->test" /*,launchProj % "test->test" */). settings(testedBaseSettings: _*). settings( name := "Compile", @@ -467,13 +433,13 @@ lazy val mavenResolverPluginProj = (project in file("sbt-maven-resolver")). def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask { val result = scriptedSource(dir => (s: State) => scriptedParser(dir)).parsed publishAll.value - doScripted((proguard in Proguard in proguardedLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, + doScripted((sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, (scalaInstance in scriptedSbtProj).value, scriptedSource.value, result, scriptedPrescripted.value) } def scriptedUnpublishedTask: Initialize[InputTask[Unit]] = Def.inputTask { val result = scriptedSource(dir => (s: State) => scriptedParser(dir)).parsed - doScripted((proguard in Proguard in proguardedLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, + doScripted((sbtLaunchJar in bundledLauncherProj).value, (fullClasspath in scriptedSbtProj in Test).value, (scalaInstance in scriptedSbtProj).value, scriptedSource.value, result, scriptedPrescripted.value) } @@ -482,8 +448,7 @@ lazy val publishLauncher = TaskKey[Unit]("publish-launcher") lazy val myProvided = config("provided") intransitive -def allProjects = Seq(launchProj, proguardedLauncherProj, - testSamples, interfaceProj, apiProj, +def allProjects = Seq(interfaceProj, apiProj, controlProj, collectionProj, applyMacroProj, processProj, ioProj, classpathProj, completeProj, logProj, relationProj, classfileProj, datatypeProj, crossProj, logicProj, ivyProj, testingProj, testAgentProj, taskProj, stdTaskProj, cacheProj, trackingProj, runProj, @@ -496,8 +461,8 @@ def projectsWithMyProvided = allProjects.map(p => p.copy(configurations = (p.con lazy val nonRoots = projectsWithMyProvided.map(p => LocalProject(p.id)) def rootSettings = Release.releaseSettings ++ fullDocSettings ++ - Util.publishPomSettings ++ otherRootSettings ++ Formatting.sbtFilesSettings ++ - Transform.conscriptSettings(launchProj) + Util.publishPomSettings ++ otherRootSettings ++ Formatting.sbtFilesSettings /*++ + Transform.conscriptSettings(launchProj)*/ def otherRootSettings = Seq( Scripted.scriptedPrescripted := { _ => }, Scripted.scripted <<= scriptedTask, @@ -576,7 +541,7 @@ def precompiled(scalav: String): Project = Project(id = normalize("Precompiled " lazy val safeUnitTests = taskKey[Unit]("Known working tests (for both 2.10 and 2.11)") lazy val safeProjects: ScopeFilter = ScopeFilter( - inProjects(launchProj, mainSettingsProj, mainProj, ivyProj, completeProj, + inProjects(mainSettingsProj, mainProj, ivyProj, completeProj, actionsProj, classpathProj, collectionProj, compileIncrementalProj, logProj, runProj, stdTaskProj), inConfigurations(Test) diff --git a/ivy/src/test/scala/ComponentManagerTest.scala b/ivy/src/test/scala/ComponentManagerTest.scala index 1995bb304..a4c720ed2 100644 --- a/ivy/src/test/scala/ComponentManagerTest.scala +++ b/ivy/src/test/scala/ComponentManagerTest.scala @@ -7,6 +7,11 @@ import IO.{ createDirectory, delete, touch, withTemporaryDirectory } import org.apache.ivy.util.ChecksumHelper import IfMissing.Fail +// TODO - We need to re-enable this test. Right now, we dont' have a "stub" launcher for this. +// This is testing something which uses a launcher interface, but was grabbing the underlying class directly +// when it really should, instead, be stubbing out the underyling class. + +/* object ComponentManagerTest extends Specification { val TestID = "manager-test" "Component manager" should { @@ -87,3 +92,4 @@ object ComponentManagerTest extends Specification { } } } +*/ \ No newline at end of file diff --git a/launch/interface/NOTICE b/launch/interface/NOTICE deleted file mode 100644 index a21975f0e..000000000 --- a/launch/interface/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ -Simple Build Tool: Launcher Interface Component -Copyright 2009, 2010 Mark Harrah -Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/AppConfiguration.java b/launch/interface/src/main/java/xsbti/AppConfiguration.java deleted file mode 100644 index c36064b06..000000000 --- a/launch/interface/src/main/java/xsbti/AppConfiguration.java +++ /dev/null @@ -1,10 +0,0 @@ -package xsbti; - -import java.io.File; - -public interface AppConfiguration -{ - public String[] arguments(); - public File baseDirectory(); - public AppProvider provider(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/AppMain.java b/launch/interface/src/main/java/xsbti/AppMain.java deleted file mode 100644 index b24e02212..000000000 --- a/launch/interface/src/main/java/xsbti/AppMain.java +++ /dev/null @@ -1,25 +0,0 @@ -package xsbti; - -/** - * The main entry interface for launching applications. Classes which implement this interface - * can be launched via the sbt launcher. - * - * In addition, classes can be adapted into this interface by the launcher if they have a static method - * matching one of these signatures: - * - * - public static void main(String[] args) - * - public static int main(String[] args) - * - public static xsbti.Exit main(String[] args) - * - */ -public interface AppMain -{ - /** Run the application and return the result. - * - * @param configuration The configuration used to run the application. Includes arguments and access to launcher features. - * @return - * The result of running this app. - * Note: the result can be things like "Please reboot this application". - */ - public MainResult run(AppConfiguration configuration); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/AppProvider.java b/launch/interface/src/main/java/xsbti/AppProvider.java deleted file mode 100644 index ab3914210..000000000 --- a/launch/interface/src/main/java/xsbti/AppProvider.java +++ /dev/null @@ -1,49 +0,0 @@ -package xsbti; - -import java.io.File; - -/** - * This represents an interface that can generate applications or servers. - * - * This provider grants access to launcher related features associated with - * the id. - */ -public interface AppProvider -{ - /** Returns the ScalaProvider that this AppProvider will use. */ - public ScalaProvider scalaProvider(); - /** The ID of the application that will be created by 'newMain' or 'mainClass'.*/ - public ApplicationID id(); - /** The classloader used to load this application. */ - public ClassLoader loader(); - /** Loads the class for the entry point for the application given by 'id'. - * This method will return the same class every invocation. - * That is, the ClassLoader is not recreated each call. - * @deprecated("use entryPoint instead") - * - * Note: This will throw an exception if the launched application does not extend AppMain. - */ - @Deprecated - public Class mainClass(); - /** Loads the class for the entry point for the application given by 'id'. - * This method will return the same class every invocation. - * That is, the ClassLoader is not recreated each call. - */ - public Class entryPoint(); - /** Creates a new instance of the entry point of the application given by 'id'. - * It is NOT guaranteed that newMain().getClass() == mainClass(). - * The sbt launcher can wrap generic static main methods. In this case, there will be a wrapper class, - * and you must use the `entryPoint` method. - * @throws IncompatibleClassChangeError if the configuration used for this Application does not - * represent a launched application. - */ - public AppMain newMain(); - - /** The classpath from which the main class is loaded, excluding Scala jars.*/ - public File[] mainClasspath(); - - /** Returns a mechanism you can use to install/find/resolve components. - * A component is just a related group of files. - */ - public ComponentProvider components(); -} diff --git a/launch/interface/src/main/java/xsbti/ApplicationID.java b/launch/interface/src/main/java/xsbti/ApplicationID.java deleted file mode 100644 index 7e72b3182..000000000 --- a/launch/interface/src/main/java/xsbti/ApplicationID.java +++ /dev/null @@ -1,58 +0,0 @@ -package xsbti; - -import java.io.File; - -/** - * This represents an identification for the sbt launcher to load and run - * an sbt launched application using ivy. - */ -public interface ApplicationID -{ - /** - * @return - * The Ivy orgnaization / Maven groupId where we can find the application to launch. - */ - public String groupID(); - /** - * @return - * The ivy module name / Maven artifactId where we can find the application to launch. - */ - public String name(); - /** - * @return - * The ivy/maven version of the module we should resolve. - */ - public String version(); - - /** - * @return - * The fully qualified name of the class that extends xsbti.AppMain - */ - public String mainClass(); - /** - * @return - * Additional ivy components we should resolve with the main application artifacts. - */ - public String[] mainComponents(); - /** - * @deprecated - * This method is no longer used if the crossVersionedValue method is available. - * - * @return - * True if the application is cross-versioned by binary-compatible version string, - * False if there is no cross-versioning. - */ - @Deprecated - public boolean crossVersioned(); - - /** - * - * @since 0.13.0 - * @return - * The type of cross-versioning the launcher should use to resolve this artifact. - */ - public CrossValue crossVersionedValue(); - - /** Files to add to the application classpath. */ - public File[] classpathExtra(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/ComponentProvider.java b/launch/interface/src/main/java/xsbti/ComponentProvider.java deleted file mode 100644 index 424e31989..000000000 --- a/launch/interface/src/main/java/xsbti/ComponentProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -package xsbti; - -import java.io.File; - - -/** - * A service to locate, install and modify "Components". - * - * A component is essentially a directory and a set of files attached to a unique string id. - */ -public interface ComponentProvider -{ - /** - * @param id The component's id string. - * @return - * The "working directory" or base directory for the component. You should perform temporary work here for the component. - */ - public File componentLocation(String id); - /** - * Grab the current component definition. - * - * @param componentID The component's id string. - * @return - * The set of files attached to this component. - */ - public File[] component(String componentID); - /** - * This will define a new component using the files passed in. - * - * Note: The component will copy/move the files into a cache location. You should not use them directly, but - * look them up using the `component` method. - * - * @param componentID The component's id string - * @param components The set of files which defines the component. - * - * @throws BootException if the component is already defined. - */ - public void defineComponent(String componentID, File[] components); - /** - * Modify an existing component by adding files to it. - * - * @param componentID The component's id string - * @param components The set of new files to add to the component. - * @return true if any files were copied and false otherwise. - * - */ - public boolean addToComponent(String componentID, File[] components); - /** - * @return The lockfile you should use to ensure your component cache does not become corrupted. - * May return null if there is no lockfile for this provider. - */ - public File lockFile(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/Continue.java b/launch/interface/src/main/java/xsbti/Continue.java deleted file mode 100644 index 6c9ebf7ee..000000000 --- a/launch/interface/src/main/java/xsbti/Continue.java +++ /dev/null @@ -1,5 +0,0 @@ -package xsbti; - -/** A launched application returns an instance of this class in order to communicate to the launcher -* that the application's main thread is finished and the launcher's work is complete, but it should not exit.*/ -public interface Continue extends MainResult {} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/CrossValue.java b/launch/interface/src/main/java/xsbti/CrossValue.java deleted file mode 100644 index fbeee82cf..000000000 --- a/launch/interface/src/main/java/xsbti/CrossValue.java +++ /dev/null @@ -1,6 +0,0 @@ -package xsbti; - -public enum CrossValue -{ - Disabled, Full, Binary -} diff --git a/launch/interface/src/main/java/xsbti/Exit.java b/launch/interface/src/main/java/xsbti/Exit.java deleted file mode 100644 index 3363fce39..000000000 --- a/launch/interface/src/main/java/xsbti/Exit.java +++ /dev/null @@ -1,10 +0,0 @@ -package xsbti; - -/** - * A launched application returns an instance of this class in order to communicate to the launcher - * that the application finished and the launcher should exit with the given exit code. - */ -public interface Exit extends MainResult -{ - public int code(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/FullReload.java b/launch/interface/src/main/java/xsbti/FullReload.java deleted file mode 100644 index 8f94be65e..000000000 --- a/launch/interface/src/main/java/xsbti/FullReload.java +++ /dev/null @@ -1,19 +0,0 @@ -package xsbti; - -public final class FullReload extends RuntimeException -{ - private final String[] arguments; - private final boolean clean; - public FullReload(String[] arguments) - { - this.arguments = arguments; - this.clean = false; - } - public FullReload(String[] arguments, boolean clean) - { - this.arguments = arguments; - this.clean = clean; - } - public boolean clean() { return clean; } - public String[] arguments() { return arguments; } -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/GlobalLock.java b/launch/interface/src/main/java/xsbti/GlobalLock.java deleted file mode 100644 index 934c57235..000000000 --- a/launch/interface/src/main/java/xsbti/GlobalLock.java +++ /dev/null @@ -1,9 +0,0 @@ -package xsbti; - -import java.io.File; -import java.util.concurrent.Callable; - -public interface GlobalLock -{ - public T apply(File lockFile, Callable run); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/IvyRepository.java b/launch/interface/src/main/java/xsbti/IvyRepository.java deleted file mode 100644 index 1c7249d8a..000000000 --- a/launch/interface/src/main/java/xsbti/IvyRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package xsbti; - - import java.net.URL; - -public interface IvyRepository extends Repository -{ - String id(); - URL url(); - String ivyPattern(); - String artifactPattern(); - boolean mavenCompatible(); - boolean skipConsistencyCheck(); - boolean descriptorOptional(); -} diff --git a/launch/interface/src/main/java/xsbti/Launcher.java b/launch/interface/src/main/java/xsbti/Launcher.java deleted file mode 100644 index 528495a70..000000000 --- a/launch/interface/src/main/java/xsbti/Launcher.java +++ /dev/null @@ -1,64 +0,0 @@ -package xsbti; - - import java.io.File; - -public interface Launcher -{ - public static final int InterfaceVersion = 1; - public ScalaProvider getScala(String version); - public ScalaProvider getScala(String version, String reason); - public ScalaProvider getScala(String version, String reason, String scalaOrg); - /** - * returns an `AppProvider` which is able to resolve an application - * and instantiate its `xsbti.Main` in a new classloader. - * See [AppProvider] for more details. - * @param id The artifact coordinates of the application. - * @param version The version to resolve - */ - public AppProvider app(ApplicationID id, String version); - /** - * This returns the "top" classloader for a launched application. This classlaoder - * lives somewhere *above* that used for the application. This classloader - * is used for doing any sort of JNA/native library loads so that downstream - * loaders can share native libraries rather than run into "load-once" restrictions. - */ - public ClassLoader topLoader(); - /** - * Return the global lock for interacting with the file system. - * - * A mechanism to do file-based locking correctly on the JVM. See - * the [[GlobalLock]] class for more details. - */ - public GlobalLock globalLock(); - /** Value of the `sbt.boot.dir` property, or the default - * boot configuration defined in `boot.directory`. - */ - public File bootDirectory(); - /** Configured launcher repositories. These repositories - * are the same ones used to load the launcher. - */ - public xsbti.Repository[] ivyRepositories(); - /** These are the repositories configured by this launcher - * which should be used by the application when resolving - * further artifacts. - */ - public xsbti.Repository[] appRepositories(); - /** The user has configured the launcher with the only repositories - * it wants to use for this applciation. - */ - public boolean isOverrideRepositories(); - /** - * The value of `ivy.ivy-home` of the boot properties file. - * This defaults to the `sbt.ivy.home` property, or `~/.ivy2`. - * Use this setting in an application when using Ivy to resolve - * more artifacts. - * - * @returns a file, or null if not set. - */ - public File ivyHome(); - /** An array of the checksums that should be checked when retreiving artifacts. - * Configured via the the `ivy.checksums` section of the boot configuration. - * Defaults to sha1, md5 or the value of the `sbt.checksums` property. - */ - public String[] checksums(); -} diff --git a/launch/interface/src/main/java/xsbti/MainResult.java b/launch/interface/src/main/java/xsbti/MainResult.java deleted file mode 100644 index b6f27a680..000000000 --- a/launch/interface/src/main/java/xsbti/MainResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package xsbti; - -/** - * A launched application should return an instance of this from its 'run' method - * to communicate to the launcher what should be done now that the application - * has completed. This interface should be treated as 'sealed', with Exit and Reboot the only - * direct subtypes. - * - * @see xsbti.Exit - * @see xsbti.Reboot - */ -public interface MainResult {} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/Manage.java b/launch/interface/src/main/java/xsbti/Manage.java deleted file mode 100644 index db63e12d4..000000000 --- a/launch/interface/src/main/java/xsbti/Manage.java +++ /dev/null @@ -1,6 +0,0 @@ -package xsbti; - -enum Manage -{ - Nop, Clean, Refresh -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/MavenRepository.java b/launch/interface/src/main/java/xsbti/MavenRepository.java deleted file mode 100644 index 3d58b15e1..000000000 --- a/launch/interface/src/main/java/xsbti/MavenRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package xsbti; - - import java.net.URL; - -public interface MavenRepository extends Repository -{ - String id(); - URL url(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/Predefined.java b/launch/interface/src/main/java/xsbti/Predefined.java deleted file mode 100644 index 1d6cf65ec..000000000 --- a/launch/interface/src/main/java/xsbti/Predefined.java +++ /dev/null @@ -1,29 +0,0 @@ -package xsbti; - -public enum Predefined -{ - Local("local"), - MavenLocal("maven-local"), - MavenCentral("maven-central"), - ScalaToolsReleases("scala-tools-releases"), - ScalaToolsSnapshots("scala-tools-snapshots"), - SonatypeOSSReleases("sonatype-oss-releases"), - SonatypeOSSSnapshots("sonatype-oss-snapshots"); - - private final String label; - private Predefined(String label) { this.label = label; } - public String toString() { return label; } - - public static Predefined toValue(String s) - { - for(Predefined p : values()) - if(s.equals(p.toString())) - return p; - - StringBuilder msg = new StringBuilder("Expected one of "); - for(Predefined p : values()) - msg.append(p.toString()).append(", "); - msg.append("got '").append(s).append("'."); - throw new RuntimeException(msg.toString()); - } -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/PredefinedRepository.java b/launch/interface/src/main/java/xsbti/PredefinedRepository.java deleted file mode 100644 index 5aacfa390..000000000 --- a/launch/interface/src/main/java/xsbti/PredefinedRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package xsbti; - -public interface PredefinedRepository extends Repository -{ - Predefined id(); -} diff --git a/launch/interface/src/main/java/xsbti/Reboot.java b/launch/interface/src/main/java/xsbti/Reboot.java deleted file mode 100644 index 0d6136a53..000000000 --- a/launch/interface/src/main/java/xsbti/Reboot.java +++ /dev/null @@ -1,16 +0,0 @@ -package xsbti; - -import java.io.File; - -/** - * A launched application returns an instance of this class in order to communicate to the launcher - * that the application should be restarted. Different versions of the application and Scala can be used. - * The application can be given different arguments as well as a new working directory. - */ -public interface Reboot extends MainResult -{ - public String[] arguments(); - public File baseDirectory(); - public String scalaVersion(); - public ApplicationID app(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/Repository.java b/launch/interface/src/main/java/xsbti/Repository.java deleted file mode 100644 index 78a51fa5d..000000000 --- a/launch/interface/src/main/java/xsbti/Repository.java +++ /dev/null @@ -1,3 +0,0 @@ -package xsbti; - -public interface Repository {} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/RetrieveException.java b/launch/interface/src/main/java/xsbti/RetrieveException.java deleted file mode 100644 index f9975b809..000000000 --- a/launch/interface/src/main/java/xsbti/RetrieveException.java +++ /dev/null @@ -1,12 +0,0 @@ -package xsbti; - -public final class RetrieveException extends RuntimeException -{ - private final String version; - public RetrieveException(String version, String msg) - { - super(msg); - this.version = version; - } - public String version() { return version; } -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/ScalaProvider.java b/launch/interface/src/main/java/xsbti/ScalaProvider.java deleted file mode 100644 index 435625eee..000000000 --- a/launch/interface/src/main/java/xsbti/ScalaProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package xsbti; - -import java.io.File; - -/** Provides access to the jars and classes for a particular version of Scala.*/ -public interface ScalaProvider -{ - public Launcher launcher(); - /** The version of Scala this instance provides.*/ - public String version(); - - /** A ClassLoader that loads the classes from scala-library.jar and scala-compiler.jar.*/ - public ClassLoader loader(); - /** Returns the scala-library.jar and scala-compiler.jar for this version of Scala. */ - public File[] jars(); - - /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ - @Deprecated - public File libraryJar(); - - /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ - @Deprecated - public File compilerJar(); - - /** Creates an application provider that will use 'loader()' as the parent ClassLoader for - * the application given by 'id'. This method will retrieve the application if it has not already - * been retrieved.*/ - public AppProvider app(ApplicationID id); -} diff --git a/launch/interface/src/main/java/xsbti/Server.java b/launch/interface/src/main/java/xsbti/Server.java deleted file mode 100644 index d4eca176d..000000000 --- a/launch/interface/src/main/java/xsbti/Server.java +++ /dev/null @@ -1,36 +0,0 @@ -package xsbti; - -/** A running server. - * - * A class implementing this must: - * - * 1. Expose an HTTP port that clients can connect to, returned via the uri method. - * 2. Accept HTTP HEAD requests against the returned URI. These are used as "ping" messages to ensure - * a server is still alive, when new clients connect. - * 3. Create a new thread to execute its service - * 4. Block the calling thread until the server is shutdown via awaitTermination() - */ -public interface Server { - /** - * @return - * A URI denoting the Port which clients can connect to. - * - * Note: we use a URI so that the server can bind to different IP addresses (even a public one) if desired. - * Note: To verify that a server is "up", the sbt launcher will attempt to connect to - * this URI's address and port with a socket. If the connection is accepted, the server is assumed to - * be working. - */ - public java.net.URI uri(); - /** - * This should block the calling thread until the server is shutdown. - * - * @return - * The result that should occur from the server. - * Can be: - * - xsbti.Exit: Shutdown this launch - * - xsbti.Reboot: Restart the server - * - * - */ - public xsbti.MainResult awaitTermination(); -} \ No newline at end of file diff --git a/launch/interface/src/main/java/xsbti/ServerMain.java b/launch/interface/src/main/java/xsbti/ServerMain.java deleted file mode 100644 index da3c8ce2b..000000000 --- a/launch/interface/src/main/java/xsbti/ServerMain.java +++ /dev/null @@ -1,17 +0,0 @@ -package xsbti; - -/** The main entry point for a launched service. This allows applciations - * to instantiate server instances. - */ -public interface ServerMain { - /** - * This method should launch one or more thread(s) which run the service. After the service has - * been started, it should return the port/URI it is listening for connections on. - * - * @param configuration - * The configuration used to launch this service. - * @return - * A running server. - */ - public Server start(AppConfiguration configuration); -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/Boot.scala b/launch/src/main/scala/xsbt/boot/Boot.scala deleted file mode 100644 index c28e2e4fd..000000000 --- a/launch/src/main/scala/xsbt/boot/Boot.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import java.io.File - -// The entry point to the launcher -object Boot { - def main(args: Array[String]) { - val config = parseArgs(args) - // If we havne't exited, we set up some hooks and launch - System.clearProperty("scala.home") // avoid errors from mixing Scala versions in the same JVM - System.setProperty("jline.shutdownhook", "false") // shutdown hooks cause class loader leaks - System.setProperty("jline.esc.timeout", "0") // starts up a thread otherwise - CheckProxy() - run(config) - } - def parseArgs(args: Array[String]): LauncherArguments = { - @annotation.tailrec - def parse(args: List[String], isLocate: Boolean, remaining: List[String]): LauncherArguments = - args match { - case "--version" :: rest => - println("sbt launcher version " + Package.getPackage("xsbt.boot").getImplementationVersion) - exit(1) - case "--locate" :: rest => parse(rest, true, remaining) - case next :: rest => parse(rest, isLocate, next :: remaining) - case Nil => new LauncherArguments(remaining.reverse, isLocate) - } - parse(args.toList, false, Nil) - } - - // this arrangement is because Scala does not always properly optimize away - // the tail recursion in a catch statement - final def run(args: LauncherArguments): Unit = runImpl(args) match { - case Some(newArgs) => run(newArgs) - case None => () - } - private def runImpl(args: LauncherArguments): Option[LauncherArguments] = - try - Launch(args) map exit - catch { - case b: BootException => errorAndExit(b.toString) - case r: xsbti.RetrieveException => errorAndExit("Error: " + r.getMessage) - case r: xsbti.FullReload => Some(new LauncherArguments(r.arguments.toList, false)) - case e: Throwable => - e.printStackTrace - errorAndExit(Pre.prefixError(e.toString)) - } - - private def errorAndExit(msg: String): Nothing = - { - System.out.println(msg) - exit(1) - } - private def exit(code: Int): Nothing = - System.exit(code).asInstanceOf[Nothing] -} diff --git a/launch/src/main/scala/xsbt/boot/BootConfiguration.scala b/launch/src/main/scala/xsbt/boot/BootConfiguration.scala deleted file mode 100644 index 084a44015..000000000 --- a/launch/src/main/scala/xsbt/boot/BootConfiguration.scala +++ /dev/null @@ -1,122 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import java.io.File - -// -// [.]scala-/ [baseDirectoryName] -// lib/ [ScalaDirectoryName] -// -/ [appDirectoryName] -// -// see also ProjectProperties for the set of constants that apply to the build.properties file in a project -// The scala organization is used as a prefix in baseDirectoryName when a non-standard organization is used. -private object BootConfiguration { - // these are the Scala module identifiers to resolve/retrieve - val ScalaOrg = "org.scala-lang" - val CompilerModuleName = "scala-compiler" - val LibraryModuleName = "scala-library" - - val JUnitName = "junit" - val JAnsiVersion = "1.11" - - val SbtOrg = "org.scala-sbt" - - /** The Ivy conflict manager to use for updating.*/ - val ConflictManagerName = "latest-revision" - /** The name of the local Ivy repository, which is used when compiling sbt from source.*/ - val LocalIvyName = "local" - /** The pattern used for the local Ivy repository, which is used when compiling sbt from source.*/ - val LocalPattern = "[organisation]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]" - /** The artifact pattern used for the local Ivy repository.*/ - def LocalArtifactPattern = LocalPattern - /** The Ivy pattern used for the local Ivy repository.*/ - def LocalIvyPattern = LocalPattern - - final val FjbgPackage = "ch.epfl.lamp.fjbg." - /** The class name prefix used to hide the Scala classes used by this loader from the application */ - final val ScalaPackage = "scala." - /** The class name prefix used to hide the Ivy classes used by this loader from the application*/ - final val IvyPackage = "org.apache.ivy." - /** - * The class name prefix used to hide the launcher classes from the application. - * Note that access to xsbti classes are allowed. - */ - final val SbtBootPackage = "xsbt.boot." - /** - * The loader will check that these classes can be loaded and will assume that their presence indicates - * the Scala compiler and library have been downloaded. - */ - val TestLoadScalaClasses = "scala.Option" :: "scala.tools.nsc.Global" :: Nil - - val ScalaHomeProperty = "scala.home" - val UpdateLogName = "update.log" - val DefaultChecksums = "sha1" :: "md5" :: Nil - - val DefaultIvyConfiguration = "default" - - /** The name of the directory within the boot directory to retrieve scala to. */ - val ScalaDirectoryName = "lib" - - /** - * 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](-[classifier]).[ext]" - - val ScalaVersionPrefix = "scala-" - - /** 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 - /** The name of the directory in the boot directory to put all jars for the given version of scala in.*/ - def baseDirectoryName(scalaOrg: String, scalaVersion: Option[String]) = scalaVersion match { - case None => "other" - case Some(sv) => (if (scalaOrg == ScalaOrg) "" else scalaOrg + ".") + ScalaVersionPrefix + sv - } - - def extractScalaVersion(dir: File): Option[String] = - { - val name = dir.getName - if (name.contains(ScalaVersionPrefix)) - Some(name.substring(name.lastIndexOf(ScalaVersionPrefix) + ScalaVersionPrefix.length)) - else - None - } -} -private final class ProxyProperties( - val envURL: String, - val envUser: String, - val envPassword: String, - val sysHost: String, - val sysPort: String, - val sysUser: String, - val sysPassword: String) -private object ProxyProperties { - val http = apply("http") - val https = apply("https") - val ftp = apply("ftp") - - def apply(pre: String) = new ProxyProperties( - pre + "_proxy", - pre + "_proxy_user", - pre + "_proxy_pass", - pre + ".proxyHost", - pre + ".proxyPort", - pre + ".proxyUser", - pre + ".proxyPassword" - ) -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/Cache.scala b/launch/src/main/scala/xsbt/boot/Cache.scala deleted file mode 100644 index 7034f6a22..000000000 --- a/launch/src/main/scala/xsbt/boot/Cache.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import java.lang.ref.{ Reference, SoftReference } -import java.util.HashMap - -final class Cache[K, X, V](create: (K, X) => V) { - private[this] val delegate = new HashMap[K, Reference[V]] - def apply(k: K, x: X): V = synchronized { getFromReference(k, x, delegate.get(k)) } - private[this] def getFromReference(k: K, x: X, existingRef: Reference[V]) = if (existingRef eq null) newEntry(k, x) else get(k, x, existingRef.get) - private[this] def get(k: K, x: X, existing: V) = if (existing == null) newEntry(k, x) else existing - private[this] def newEntry(k: K, x: X): V = - { - val v = create(k, x) - Pre.assert(v != null, "Value for key " + k + " was null") - delegate.put(k, new SoftReference(v)) - v - } -} diff --git a/launch/src/main/scala/xsbt/boot/CheckProxy.scala b/launch/src/main/scala/xsbt/boot/CheckProxy.scala deleted file mode 100644 index d82409e06..000000000 --- a/launch/src/main/scala/xsbt/boot/CheckProxy.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.net.{ MalformedURLException, URL } - -object CheckProxy { - def apply() { - import ProxyProperties._ - for (pp <- Seq(http, https, ftp)) - setFromEnv(pp) - } - - private[this] def setFromEnv(conf: ProxyProperties) { - import conf._ - val proxyURL = System.getenv(envURL) - if (isDefined(proxyURL) && !isPropertyDefined(sysHost) && !isPropertyDefined(sysPort)) { - try { - val proxy = new URL(proxyURL) - setProperty(sysHost, proxy.getHost) - val port = proxy.getPort - if (port >= 0) - System.setProperty(sysPort, port.toString) - copyEnv(envUser, sysUser) - copyEnv(envPassword, sysPassword) - } catch { - case e: MalformedURLException => - System.out.println(s"Warning: could not parse $envURL setting: ${e.toString}") - } - } - } - - private def copyEnv(envKey: String, sysKey: String) { setProperty(sysKey, System.getenv(envKey)) } - private def setProperty(key: String, value: String) { if (value != null) System.setProperty(key, value) } - private def isPropertyDefined(k: String) = isDefined(System.getProperty(k)) - private def isDefined(s: String) = s != null && isNonEmpty(s) -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/Configuration.scala b/launch/src/main/scala/xsbt/boot/Configuration.scala deleted file mode 100644 index f1c08e766..000000000 --- a/launch/src/main/scala/xsbt/boot/Configuration.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.io.{ File, FileInputStream, InputStreamReader } -import java.net.{ MalformedURLException, URI, URL } -import java.util.regex.Pattern -import scala.collection.immutable.List -import annotation.tailrec - -object ConfigurationStorageState extends Enumeration { - val PropertiesFile = value("properties-file") - val SerializedFile = value("serialized-file") -} - -object Configuration { - import ConfigurationStorageState._ - final val SysPropPrefix = "-D" - def parse(file: URL, baseDirectory: File) = Using(new InputStreamReader(file.openStream, "utf8"))((new ConfigurationParser).apply) - - /** - * Finds the configuration location. - * - * Note: Configuration may be previously serialized by a launcher. - */ - @tailrec def find(args: List[String], baseDirectory: File): (URL, List[String], ConfigurationStorageState.Value) = - args match { - case head :: tail if head.startsWith("@load:") => (directConfiguration(head.substring(6), baseDirectory), tail, SerializedFile) - case head :: tail if head.startsWith("@") => (directConfiguration(head.substring(1), baseDirectory), tail, PropertiesFile) - case head :: tail if head.startsWith(SysPropPrefix) => - setProperty(head stripPrefix SysPropPrefix) - find(tail, baseDirectory) - case _ => - val propertyConfigured = System.getProperty("sbt.boot.properties") - val url = if (propertyConfigured == null) configurationOnClasspath else configurationFromFile(propertyConfigured, baseDirectory) - (url, args, PropertiesFile) - } - def setProperty(head: String) { - val keyValue = head.split("=", 2) - if (keyValue.length != 2) - System.err.println("Warning: invalid system property '" + head + "'") - else - System.setProperty(keyValue(0), keyValue(1)) - } - def configurationOnClasspath: URL = - { - val paths = resourcePaths(guessSbtVersion) - paths.iterator.map(getClass.getResource).find(neNull) getOrElse - (multiPartError("Could not finder sbt launch configuration. Searched classpath for:", paths)) - } - def directConfiguration(path: String, baseDirectory: File): URL = - { - try { new URL(path) } - catch { case _: MalformedURLException => configurationFromFile(path, baseDirectory) } - } - def configurationFromFile(path: String, baseDirectory: File): URL = - { - val pathURI = filePathURI(path) - def resolve(against: URI): Option[URL] = - { - val resolved = against.resolve(pathURI) // variant that accepts String doesn't properly escape (#725) - val exists = try { (new File(resolved)).exists } catch { case _: IllegalArgumentException => false } - if (exists) Some(resolved.toURL) else None - } - val against = resolveAgainst(baseDirectory) - // use Iterators so that resolution occurs lazily, for performance - val resolving = against.iterator.flatMap(e => resolve(e).toList.iterator) - if (!resolving.hasNext) multiPartError("Could not find configuration file '" + path + "'. Searched:", against) - resolving.next() - } - def multiPartError[T](firstLine: String, lines: List[T]) = Pre.error((firstLine :: lines).mkString("\n\t")) - - def UnspecifiedVersionPart = "Unspecified" - def DefaultVersionPart = "Default" - def DefaultBuildProperties = "project/build.properties" - def SbtVersionProperty = "sbt.version" - val ConfigurationName = "sbt.boot.properties" - val JarBasePath = "/sbt/" - def userConfigurationPath = "/" + ConfigurationName - def defaultConfigurationPath = JarBasePath + ConfigurationName - val baseResourcePaths: List[String] = userConfigurationPath :: defaultConfigurationPath :: Nil - def resourcePaths(sbtVersion: Option[String]): List[String] = - versionParts(sbtVersion) flatMap { part => - baseResourcePaths map { base => - base + part - } - } - def fallbackParts: List[String] = "" :: Nil - def versionParts(version: Option[String]): List[String] = - version match { - case None => UnspecifiedVersionPart :: fallbackParts - case Some(v) => versionParts(v) - } - def versionParts(version: String): List[String] = - { - val pattern = Pattern.compile("""(\d+)(\.\d+)(\.\d+)(-.*)?""") - val m = pattern.matcher(version) - if (m.matches()) - subPartsIndices flatMap { is => fullMatchOnly(is.map(m.group)) } - else - noMatchParts - } - def noMatchParts: List[String] = DefaultVersionPart :: fallbackParts - private[this] def fullMatchOnly(groups: List[String]): Option[String] = - if (groups.forall(neNull)) Some(groups.mkString) else None - - private[this] def subPartsIndices = - (1 :: 2 :: 3 :: 4 :: Nil) :: - (1 :: 2 :: 3 :: Nil) :: - (1 :: 2 :: Nil) :: - (Nil) :: - Nil - - // the location of project/build.properties and the name of the property within that file - // that configures the sbt version is configured in sbt.boot.properties. - // We have to hard code them here in order to use them to determine the location of sbt.boot.properties itself - def guessSbtVersion: Option[String] = - { - val props = Pre.readProperties(new File(DefaultBuildProperties)) - Option(props.getProperty(SbtVersionProperty)) - } - - def resolveAgainst(baseDirectory: File): List[URI] = - directoryURI(baseDirectory) :: - directoryURI(new File(System.getProperty("user.home"))) :: - toDirectory(classLocation(getClass).toURI) :: - Nil - - def classLocation(cl: Class[_]): URL = - { - val codeSource = cl.getProtectionDomain.getCodeSource - if (codeSource == null) Pre.error("No class location for " + cl) - else codeSource.getLocation - } - // single-arg constructor doesn't properly escape - def filePathURI(path: String): URI = { - if (path.startsWith("file:")) new URI(path) - else { - val f = new File(path) - new URI(if (f.isAbsolute) "file" else null, path, null) - } - } - def directoryURI(dir: File): URI = directoryURI(dir.toURI) - def directoryURI(uri: URI): URI = - { - assert(uri.isAbsolute) - val str = uri.toASCIIString - val dirStr = if (str.endsWith("/")) str else str + "/" - (new URI(dirStr)).normalize - } - - def toDirectory(uri: URI): URI = - try { - val file = new File(uri) - val newFile = if (file.isFile) file.getParentFile else file - directoryURI(newFile) - } catch { case _: Exception => uri } - private[this] def neNull: AnyRef => Boolean = _ ne null -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/ConfigurationParser.scala b/launch/src/main/scala/xsbt/boot/ConfigurationParser.scala deleted file mode 100644 index 9e4fc54a0..000000000 --- a/launch/src/main/scala/xsbt/boot/ConfigurationParser.scala +++ /dev/null @@ -1,306 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import ConfigurationParser._ -import java.lang.Character.isWhitespace -import java.io.{ BufferedReader, File, FileInputStream, InputStreamReader, Reader, StringReader } -import java.net.{ MalformedURLException, URL } -import java.util.regex.{ Matcher, Pattern } -import Matcher.quoteReplacement -import scala.collection.immutable.List - -object ConfigurationParser { - def trim(s: Array[String]) = s.map(_.trim).toList - def ids(value: String) = trim(substituteVariables(value).split(",")).filter(isNonEmpty) - - private[this] lazy val VarPattern = Pattern.compile("""\$\{([\w.]+)(\-(.*))?\}""") - def substituteVariables(s: String): String = if (s.indexOf('$') >= 0) substituteVariables0(s) else s - // scala.util.Regex brought in 30kB, so we code it explicitly - def substituteVariables0(s: String): String = - { - val m = VarPattern.matcher(s) - val b = new StringBuffer - while (m.find()) { - val key = m.group(1) - val defined = System.getProperty(key) - val value = - if (defined ne null) - defined - else { - val default = m.group(3) - if (default eq null) m.group() else substituteVariables(default) - } - m.appendReplacement(b, quoteReplacement(value)) - } - m.appendTail(b) - b.toString - } - - implicit val readIDs = ids _ -} -class ConfigurationParser { - def apply(file: File): LaunchConfiguration = Using(newReader(file))(apply) - def apply(s: String): LaunchConfiguration = Using(new StringReader(s))(apply) - def apply(reader: Reader): LaunchConfiguration = Using(new BufferedReader(reader))(apply) - private def apply(in: BufferedReader): LaunchConfiguration = - processSections(processLines(readLine(in, Nil, 0))) - private final def readLine(in: BufferedReader, accum: List[Line], index: Int): List[Line] = - in.readLine match { - case null => accum.reverse - case line => readLine(in, ParseLine(line, index) ::: accum, index + 1) - } - private def newReader(file: File) = new InputStreamReader(new FileInputStream(file), "UTF-8") - def readRepositoriesConfig(file: File): List[Repository.Repository] = - Using(newReader(file))(readRepositoriesConfig) - def readRepositoriesConfig(reader: Reader): List[Repository.Repository] = - Using(new BufferedReader(reader))(readRepositoriesConfig) - def readRepositoriesConfig(s: String): List[Repository.Repository] = - Using(new StringReader(s))(readRepositoriesConfig) - private def readRepositoriesConfig(in: BufferedReader): List[Repository.Repository] = - processRepositoriesConfig(processLines(readLine(in, Nil, 0))) - def processRepositoriesConfig(sections: SectionMap): List[Repository.Repository] = - processSection(sections, "repositories", getRepositories)._1 - // section -> configuration instance processing - def processSections(sections: SectionMap): LaunchConfiguration = - { - val ((scalaVersion, scalaClassifiers), m1) = processSection(sections, "scala", getScala) - val ((app, appClassifiers), m2) = processSection(m1, "app", getApplication) - val (defaultRepositories, 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) - val ((ivyHome, checksums, isOverrideRepos, rConfigFile), m7) = processSection(m6, "ivy", getIvy) - val (serverOptions, m8) = processSection(m7, "server", getServer) - check(m8, "section") - val classifiers = Classifiers(scalaClassifiers, appClassifiers) - val repositories = rConfigFile map readRepositoriesConfig getOrElse defaultRepositories - val ivyOptions = IvyOptions(ivyHome, classifiers, repositories, checksums, isOverrideRepos) - - // TODO - Read server properties... - new LaunchConfiguration(scalaVersion, ivyOptions, app, boot, logging, properties, serverOptions) - } - def getScala(m: LabelMap) = - { - val (scalaVersion, m1) = getVersion(m, "Scala version", "scala.version") - val (scalaClassifiers, m2) = getClassifiers(m1, "Scala classifiers") - check(m2, "label") - (scalaVersion, scalaClassifiers) - } - def getClassifiers(m: LabelMap, label: String): (Value[List[String]], LabelMap) = - process(m, "classifiers", processClassifiers(label)) - def processClassifiers(label: String)(value: Option[String]): Value[List[String]] = - value.map(readValue[List[String]](label)) getOrElse new Explicit(Nil) - - def getVersion(m: LabelMap, label: String, defaultName: String): (Value[String], LabelMap) = process(m, "version", processVersion(label, defaultName)) - def processVersion(label: String, defaultName: String)(value: Option[String]): Value[String] = - value.map(readValue[String](label)).getOrElse(new Implicit(defaultName, None)) - - def readValue[T](label: String)(implicit read: String => T): String => Value[T] = value0 => - { - val value = substituteVariables(value0) - if (isEmpty(value)) Pre.error(label + " cannot be empty (omit declaration to use the default)") - try { parsePropertyValue(label, value)(Value.readImplied[T]) } - catch { case e: BootException => new Explicit(read(value)) } - } - def processSection[T](sections: SectionMap, name: String, f: LabelMap => T) = - process[String, LabelMap, T](sections, name, m => f(m default (x => None))) - def process[K, V, T](sections: ListMap[K, V], name: K, f: V => T): (T, ListMap[K, V]) = (f(sections(name)), sections - name) - def check(map: ListMap[String, _], label: String): Unit = if (map.isEmpty) () else Pre.error(map.keys.mkString("Invalid " + label + "(s): ", ",", "")) - def check[T](label: String, pair: (T, ListMap[String, _])): T = { check(pair._2, label); pair._1 } - def id(map: LabelMap, name: String, default: String): (String, LabelMap) = - (substituteVariables(orElse(getOrNone(map, name), default)), map - name) - def getOrNone[K, V](map: ListMap[K, Option[V]], k: K) = orElse(map.get(k), None) - def ids(map: LabelMap, name: String, default: List[String]) = - { - val result = map(name) map ConfigurationParser.ids - (orElse(result, default), map - name) - } - def bool(map: LabelMap, name: String, default: Boolean): (Boolean, LabelMap) = - { - val (b, m) = id(map, name, default.toString) - (toBoolean(b), m) - } - - def toFiles(paths: List[String]): List[File] = paths.map(toFile) - def toFile(path: String): File = new File(substituteVariables(path).replace('/', File.separatorChar)) // if the path is relative, it will be resolved by Launch later - def file(map: LabelMap, name: String, default: File): (File, LabelMap) = - (orElse(getOrNone(map, name).map(toFile), default), map - name) - def optfile(map: LabelMap, name: String): (Option[File], LabelMap) = - (getOrNone(map, name).map(toFile), map - name) - def getIvy(m: LabelMap): (Option[File], List[String], Boolean, Option[File]) = - { - val (ivyHome, m1) = optfile(m, "ivy-home") - val (checksums, m2) = ids(m1, "checksums", BootConfiguration.DefaultChecksums) - val (overrideRepos, m3) = bool(m2, "override-build-repos", false) - val (repoConfig, m4) = optfile(m3, "repository-config") - check(m4, "label") - (ivyHome, checksums, overrideRepos, repoConfig filter (_.exists)) - } - def getBoot(m: LabelMap): BootSetup = - { - val (dir, m1) = file(m, "directory", toFile("project/boot")) - val (props, m2) = file(m1, "properties", toFile("project/build.properties")) - val (search, m3) = getSearch(m2, props) - val (enableQuick, m4) = bool(m3, "quick-option", false) - val (promptFill, m5) = bool(m4, "prompt-fill", false) - val (promptCreate, m6) = id(m5, "prompt-create", "") - val (lock, m7) = bool(m6, "lock", true) - check(m7, "label") - BootSetup(dir, lock, props, search, promptCreate, enableQuick, promptFill) - } - def getLogging(m: LabelMap): Logging = check("label", process(m, "level", getLevel)) - def getLevel(m: Option[String]) = m.map(LogLevel.apply).getOrElse(new Logging(LogLevel.Info)) - def getSearch(m: LabelMap, defaultPath: File): (Search, LabelMap) = - ids(m, "search", Nil) match { - case (Nil, newM) => (Search.none, newM) - case (tpe :: Nil, newM) => (Search(tpe, List(defaultPath)), newM) - case (tpe :: paths, newM) => (Search(tpe, toFiles(paths)), newM) - } - - def getApplication(m: LabelMap): (Application, Value[List[String]]) = - { - val (org, m1) = id(m, "org", BootConfiguration.SbtOrg) - val (name, m2) = id(m1, "name", "sbt") - val (rev, m3) = getVersion(m2, name + " version", name + ".version") - val (main, m4) = id(m3, "class", "xsbt.Main") - val (components, m5) = ids(m4, "components", List("default")) - val (crossVersioned, m6) = id(m5, "cross-versioned", CrossVersionUtil.binaryString) - val (resources, m7) = ids(m6, "resources", Nil) - val (classifiers, m8) = getClassifiers(m7, "Application classifiers") - check(m8, "label") - val classpathExtra = toArray(toFiles(resources)) - val app = new Application(org, name, rev, main, components, LaunchCrossVersion(crossVersioned), classpathExtra) - (app, classifiers) - } - def getServer(m: LabelMap): (Option[ServerConfiguration]) = - { - val (lock, m1) = optfile(m, "lock") - // TODO - JVM args - val (args, m2) = optfile(m1, "jvmargs") - val (props, m3) = optfile(m2, "jvmprops") - lock map { file => - ServerConfiguration(file, args, props) - } - } - def getRepositories(m: LabelMap): List[Repository.Repository] = - { - import Repository.{ Ivy, Maven, Predefined } - val BootOnly = "bootOnly" - val MvnComp = "mavenCompatible" - val DescriptorOptional = "descriptorOptional" - val DontCheckConsistency = "skipConsistencyCheck" - val OptSet = Set(BootOnly, MvnComp, DescriptorOptional, DontCheckConsistency) - m.toList.map { - case (key, None) => Predefined(key) - case (key, Some(BootOnly)) => Predefined(key, true) - case (key, Some(value)) => - val r = trim(substituteVariables(value).split(",", 7)) - val url = try { new URL(r(0)) } catch { case e: MalformedURLException => Pre.error("Invalid URL specified for '" + key + "': " + e.getMessage) } - val (optionPart, patterns) = r.tail.partition(OptSet.contains(_)) - val options = (optionPart.contains(BootOnly), optionPart.contains(MvnComp), optionPart.contains(DescriptorOptional), optionPart.contains(DontCheckConsistency)) - (patterns, options) match { - case (both :: Nil, (bo, mc, dso, cc)) => Ivy(key, url, both, both, mavenCompatible = mc, bootOnly = bo, descriptorOptional = dso, skipConsistencyCheck = cc) - case (ivy :: art :: Nil, (bo, mc, dso, cc)) => Ivy(key, url, ivy, art, mavenCompatible = mc, bootOnly = bo, descriptorOptional = dso, skipConsistencyCheck = cc) - case (Nil, (true, false, false, cc)) => Maven(key, url, bootOnly = true) - case (Nil, (false, false, false, false)) => Maven(key, url) - case _ => Pre.error("Could not parse %s: %s".format(key, value)) - } - } - } - def getAppProperties(m: LabelMap): List[AppProperty] = - for ((name, Some(value)) <- m.toList) yield { - val map = ListMap(trim(value.split(",")).map(parsePropertyDefinition(name)): _*) - AppProperty(name)(map.get("quick"), map.get("new"), map.get("fill")) - } - def parsePropertyDefinition(name: String)(value: String) = value.split("=", 2) match { - case Array(mode, value) => (mode, parsePropertyValue(name, value)(defineProperty(name))) - case x => Pre.error("Invalid property definition '" + x + "' for property '" + name + "'") - } - def defineProperty(name: String)(action: String, requiredArg: String, optionalArg: Option[String]) = - action match { - case "prompt" => new PromptProperty(requiredArg, optionalArg) - case "set" => new SetProperty(requiredArg) - case _ => Pre.error("Unknown action '" + action + "' for property '" + name + "'") - } - private[this] lazy val propertyPattern = Pattern.compile("""(.+)\((.*)\)(?:\[(.*)\])?""") // examples: prompt(Version)[1.0] or set(1.0) - def parsePropertyValue[T](name: String, definition: String)(f: (String, String, Option[String]) => T): T = - { - val m = propertyPattern.matcher(definition) - if (!m.matches()) Pre.error("Invalid property definition '" + definition + "' for property '" + name + "'") - val optionalArg = m.group(3) - f(m.group(1), m.group(2), if (optionalArg eq null) None else Some(optionalArg)) - } - - type LabelMap = ListMap[String, Option[String]] - // section-name -> label -> value - type SectionMap = ListMap[String, LabelMap] - def processLines(lines: List[Line]): SectionMap = - { - type State = (SectionMap, Option[String]) - val s: State = - (((ListMap.empty.default(x => ListMap.empty[String, Option[String]]), None): State) /: lines) { - case (x, Comment) => x - case ((map, _), s: Section) => (map, Some(s.name)) - case ((_, None), l: Labeled) => Pre.error("Label " + l.label + " is not in a section") - case ((map, s @ Some(section)), l: Labeled) => - val sMap = map(section) - if (sMap.contains(l.label)) Pre.error("Duplicate label '" + l.label + "' in section '" + section + "'") - else (map(section) = (sMap(l.label) = l.value), s) - } - s._1 - } - -} - -sealed trait Line -final class Labeled(val label: String, val value: Option[String]) extends Line -final class Section(val name: String) extends Line -object Comment extends Line - -class ParseException(val content: String, val line: Int, val col: Int, val msg: String) - extends BootException("[" + (line + 1) + ", " + (col + 1) + "]" + msg + "\n" + content + "\n" + List.fill(col)(" ").mkString + "^") - -object ParseLine { - def apply(content: String, line: Int) = - { - def error(col: Int, msg: String) = throw new ParseException(content, line, col, msg) - def check(condition: Boolean)(col: Int, msg: String) = if (condition) () else error(col, msg) - - val trimmed = trimLeading(content) - val offset = content.length - trimmed.length - - def section = - { - val closing = trimmed.indexOf(']', 1) - check(closing > 0)(content.length, "Expected ']', found end of line") - val extra = trimmed.substring(closing + 1) - val trimmedExtra = trimLeading(extra) - check(isEmpty(trimmedExtra))(content.length - trimmedExtra.length, "Expected end of line, found '" + extra + "'") - new Section(trimmed.substring(1, closing).trim) - } - def labeled = - { - trimmed.split(":", 2) match { - case Array(label, value) => - val trimmedValue = value.trim - check(isNonEmpty(trimmedValue))(content.indexOf(':'), "Value for '" + label + "' was empty") - new Labeled(label, Some(trimmedValue)) - case x => new Labeled(x.mkString, None) - } - } - - if (isEmpty(trimmed)) Nil - else { - val processed = - trimmed.charAt(0) match { - case '#' => Comment - case '[' => section - case _ => labeled - } - processed :: Nil - } - } -} diff --git a/launch/src/main/scala/xsbt/boot/Create.scala b/launch/src/main/scala/xsbt/boot/Create.scala deleted file mode 100644 index f09a4dac2..000000000 --- a/launch/src/main/scala/xsbt/boot/Create.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.io.{ File, FileInputStream, FileOutputStream } -import java.util.{ Locale, Properties } -import scala.collection.immutable.List - -object Initialize { - lazy val selectCreate = (_: AppProperty).create - lazy val selectQuick = (_: AppProperty).quick - lazy val selectFill = (_: AppProperty).fill - def create(file: File, promptCreate: String, enableQuick: Boolean, spec: List[AppProperty]) { - readLine(promptCreate + " (y/N" + (if (enableQuick) "/s" else "") + ") ") match { - case None => declined("") - case Some(line) => - line.toLowerCase(Locale.ENGLISH) match { - case "y" | "yes" => process(file, spec, selectCreate) - case "s" => process(file, spec, selectQuick) - case "n" | "no" | "" => declined("") - case x => - System.out.println(" '" + x + "' not understood.") - create(file, promptCreate, enableQuick, spec) - } - } - } - def fill(file: File, spec: List[AppProperty]): Unit = process(file, spec, selectFill) - def process(file: File, appProperties: List[AppProperty], select: AppProperty => Option[PropertyInit]) { - val properties = readProperties(file) - val uninitialized = - for (property <- appProperties; init <- select(property) if properties.getProperty(property.name) == null) yield initialize(properties, property.name, init) - if (uninitialized.nonEmpty) writeProperties(properties, file, "") - } - def initialize(properties: Properties, name: String, init: PropertyInit) { - init match { - case set: SetProperty => properties.setProperty(name, set.value) - case prompt: PromptProperty => - def noValue = declined("No value provided for " + prompt.label) - readLine(prompt.label + prompt.default.toList.map(" [" + _ + "]").mkString + ": ") match { - case None => noValue - case Some(line) => - val value = if (isEmpty(line)) orElse(prompt.default, noValue) else line - properties.setProperty(name, value) - } - } - } -} diff --git a/launch/src/main/scala/xsbt/boot/Enumeration.scala b/launch/src/main/scala/xsbt/boot/Enumeration.scala deleted file mode 100644 index 49e90ea1d..000000000 --- a/launch/src/main/scala/xsbt/boot/Enumeration.scala +++ /dev/null @@ -1,26 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008,2009 David MacIver, Mark Harrah - */ -package xsbt.boot - -import Pre._ -import scala.collection.immutable.List - -class Enumeration extends Serializable { - def elements: List[Value] = members - private lazy val members: List[Value] = - { - val c = getClass - val correspondingFields = ListMap(c.getDeclaredFields.map(f => (f.getName, f)): _*) - c.getMethods.toList flatMap { method => - if (method.getParameterTypes.length == 0 && classOf[Value].isAssignableFrom(method.getReturnType)) { - for (field <- correspondingFields.get(method.getName) if field.getType == method.getReturnType) yield method.invoke(this).asInstanceOf[Value] - } else - Nil - } - } - def value(s: String) = new Value(s, 0) - def value(s: String, i: Int) = new Value(s, i) - final class Value(override val toString: String, val id: Int) extends Serializable - 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/src/main/scala/xsbt/boot/Exceptions.scala b/launch/src/main/scala/xsbt/boot/Exceptions.scala deleted file mode 100644 index ddb86810e..000000000 --- a/launch/src/main/scala/xsbt/boot/Exceptions.scala +++ /dev/null @@ -1,9 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.boot - -// The exception to use when an error occurs at the launcher level (and not a nested exception). -// This indicates overrides toString because the exception class name is not needed to understand -// the error message. -class BootException(override val toString: String) extends RuntimeException(toString) diff --git a/launch/src/main/scala/xsbt/boot/FilteredLoader.scala b/launch/src/main/scala/xsbt/boot/FilteredLoader.scala deleted file mode 100644 index 860a488e3..000000000 --- a/launch/src/main/scala/xsbt/boot/FilteredLoader.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.boot - -import BootConfiguration.{ FjbgPackage, IvyPackage, SbtBootPackage, ScalaPackage } -import scala.collection.immutable.Stream - -/** - * A custom class loader to ensure the main part of sbt doesn't load any Scala or - * Ivy classes from the jar containing the loader. - */ -private[boot] final class BootFilteredLoader(parent: ClassLoader) extends ClassLoader(parent) { - @throws(classOf[ClassNotFoundException]) - override final def loadClass(className: String, resolve: Boolean): Class[_] = - { - // note that we allow xsbti.* - if (className.startsWith(ScalaPackage) || className.startsWith(IvyPackage) || className.startsWith(SbtBootPackage) || className.startsWith(FjbgPackage)) - throw new ClassNotFoundException(className) - else - super.loadClass(className, resolve) - } - override def getResources(name: String) = excludedLoader.getResources(name) - override def getResource(name: String) = excludedLoader.getResource(name) - - // the loader to use when a resource is excluded. This needs to be at least parent.getParent so that it skips parent. parent contains - // resources included in the launcher, which need to be ignored. Now that the launcher can be unrooted (not the application entry point), - // this needs to be the Java extension loader (the loader with getParent == null) - private val excludedLoader = Loaders(parent.getParent).head -} - -object Loaders { - def apply(loader: ClassLoader): Stream[ClassLoader] = - { - def loaders(loader: ClassLoader, accum: Stream[ClassLoader]): Stream[ClassLoader] = - if (loader eq null) accum else loaders(loader.getParent, Stream.cons(loader, accum)) - loaders(loader, Stream.empty) - } -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/Find.scala b/launch/src/main/scala/xsbt/boot/Find.scala deleted file mode 100644 index fddd451c4..000000000 --- a/launch/src/main/scala/xsbt/boot/Find.scala +++ /dev/null @@ -1,55 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.io.File -import java.net.URI -import scala.collection.immutable.List - -object Find { def apply(config: LaunchConfiguration, currentDirectory: File) = (new Find(config))(currentDirectory) } -class Find(config: LaunchConfiguration) { - import config.boot.search - def apply(currentDirectory: File) = - { - val current = currentDirectory.getCanonicalFile - assert(current.isDirectory) - - lazy val fromRoot = path(current, Nil).filter(hasProject).map(_.getCanonicalFile) - val found: Option[File] = - search.tpe match { - case Search.RootFirst => fromRoot.headOption - case Search.Nearest => fromRoot.lastOption - case Search.Only => - if (hasProject(current)) - Some(current) - else - fromRoot match { - case Nil => Some(current) - case head :: Nil => Some(head) - case xs => - System.err.println("Search method is 'only' and multiple ancestor directories match:\n\t" + fromRoot.mkString("\n\t")) - System.exit(1) - None - } - case _ => Some(current) - } - val baseDirectory = orElse(found, current) - System.setProperty("user.dir", baseDirectory.getAbsolutePath) - (ResolvePaths(config, baseDirectory), baseDirectory) - } - private def hasProject(f: File) = f.isDirectory && search.paths.forall(p => ResolvePaths(f, p).exists) - private def path(f: File, acc: List[File]): List[File] = if (f eq null) acc else path(f.getParentFile, f :: acc) -} -object ResolvePaths { - def apply(config: LaunchConfiguration, baseDirectory: File): LaunchConfiguration = - config.map(f => apply(baseDirectory, f)) - def apply(baseDirectory: File, f: File): File = - if (f.isAbsolute) f - else { - assert(baseDirectory.isDirectory) // if base directory is not a directory, URI.resolve will not work properly - val uri = new URI(null, null, f.getPath, null) - new File(baseDirectory.toURI.resolve(uri)) - } -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/JAnsi.scala b/launch/src/main/scala/xsbt/boot/JAnsi.scala deleted file mode 100644 index 70f160341..000000000 --- a/launch/src/main/scala/xsbt/boot/JAnsi.scala +++ /dev/null @@ -1,23 +0,0 @@ -package xsbt.boot - -import Pre._ - -object JAnsi { - def uninstall(loader: ClassLoader): Unit = callJAnsi("systemUninstall", loader) - def install(loader: ClassLoader): Unit = callJAnsi("systemInstall", loader) - - private[this] def callJAnsi(methodName: String, loader: ClassLoader): Unit = if (isWindows && !isCygwin) callJAnsiMethod(methodName, loader) - private[this] def callJAnsiMethod(methodName: String, loader: ClassLoader): Unit = - try { - val c = Class.forName("org.fusesource.jansi.AnsiConsole", true, loader) - c.getMethod(methodName).invoke(null) - } catch { - case ignore: ClassNotFoundException => - /* The below code intentionally traps everything. It technically shouldn't trap the - * non-StackOverflowError VirtualMachineErrors and AWTError would be weird, but this is PermGen - * mitigation code that should not render sbt completely unusable if jansi initialization fails. - * [From Mark Harrah, https://github.com/sbt/sbt/pull/633#issuecomment-11957578]. - */ - case ex: Throwable => println("Jansi found on class path but initialization failed: " + ex) - } -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/Launch.scala b/launch/src/main/scala/xsbt/boot/Launch.scala deleted file mode 100644 index 8e8863457..000000000 --- a/launch/src/main/scala/xsbt/boot/Launch.scala +++ /dev/null @@ -1,398 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010, 2011 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import BootConfiguration.{ CompilerModuleName, JAnsiVersion, LibraryModuleName } -import java.io.File -import java.net.{ URL, URLClassLoader, URI } -import java.util.concurrent.Callable -import scala.collection.immutable.List -import scala.annotation.tailrec -import ConfigurationStorageState._ - -class LauncherArguments(val args: List[String], val isLocate: Boolean) - -object Launch { - def apply(arguments: LauncherArguments): Option[Int] = apply((new File("")).getAbsoluteFile, arguments) - - def apply(currentDirectory: File, arguments: LauncherArguments): Option[Int] = { - val (configLocation, newArgs2, state) = Configuration.find(arguments.args, currentDirectory) - val config = state match { - case SerializedFile => LaunchConfiguration.restore(configLocation) - case PropertiesFile => parseAndInitializeConfig(configLocation, currentDirectory) - } - if (arguments.isLocate) { - if (newArgs2.nonEmpty) { - // TODO - Print the arguments without exploding proguard size. - System.err.println("Warning: --locate option ignores arguments.") - } - locate(currentDirectory, config) - } else { - // First check to see if there are java system properties we need to set. Then launch the application. - updateProperties(config) - launch(run(Launcher(config)))(makeRunConfig(currentDirectory, config, newArgs2)) - } - } - /** Locate a server, print where it is, and exit. */ - def locate(currentDirectory: File, config: LaunchConfiguration): Option[Int] = { - config.serverConfig match { - case Some(_) => - val uri = ServerLocator.locate(currentDirectory, config) - System.out.println(uri.toASCIIString) - Some(0) - case None => sys.error(s"${config.app.groupID}-${config.app.main} is not configured as a server.") - } - } - /** - * Some hackery to allow sys.props to be configured via a file. If this launch config has - * a valid file configured, we load the properties and and apply them to this jvm. - */ - def updateProperties(config: LaunchConfiguration): Unit = { - config.serverConfig match { - case Some(config) => - config.jvmPropsFile match { - case Some(file) if file.exists => - try setSystemProperties(readProperties(file)) - catch { - case e: Exception => throw new RuntimeException(s"Unable to load server properties file: ${file}", e) - } - case _ => - } - case None => - } - } - - /** Parses the configuration *and* runs the initialization code that will remove variable references. */ - def parseAndInitializeConfig(configLocation: URL, currentDirectory: File): LaunchConfiguration = - { - val (parsed, bd) = parseConfiguration(configLocation, currentDirectory) - resolveConfig(parsed) - } - /** Parse configuration and return it and the baseDirectory of the launch. */ - def parseConfiguration(configLocation: URL, currentDirectory: File): (LaunchConfiguration, File) = - Find(Configuration.parse(configLocation, currentDirectory), currentDirectory) - - /** Setups the Initialize object so we can fill in system properties in the configuration */ - def resolveConfig(parsed: LaunchConfiguration): LaunchConfiguration = - { - // Set up initialize. - val propertiesFile = parsed.boot.properties - import parsed.boot.{ enableQuick, promptCreate, promptFill } - if (isNonEmpty(promptCreate) && !propertiesFile.exists) - Initialize.create(propertiesFile, promptCreate, enableQuick, parsed.appProperties) - else if (promptFill) - Initialize.fill(propertiesFile, parsed.appProperties) - - parsed.logging.debug("Parsed configuration: " + parsed) - val resolved = ResolveValues(parsed) - resolved.logging.debug("Resolved configuration: " + resolved) - resolved - } - - /** Create run configuration we'll use to launch the app. */ - def makeRunConfig(currentDirectory: File, config: LaunchConfiguration, arguments: List[String]): RunConfiguration = - new RunConfiguration(config.getScalaVersion, config.app.toID, currentDirectory, arguments) - - /** The actual mechanism used to run a launched application. */ - def run(launcher: xsbti.Launcher)(config: RunConfiguration): xsbti.MainResult = - { - import config._ - val appProvider: xsbti.AppProvider = launcher.app(app, orNull(scalaVersion)) // takes ~40 ms when no update is required - val appConfig: xsbti.AppConfiguration = new AppConfiguration(toArray(arguments), workingDirectory, appProvider) - - // TODO - Jansi probably should be configurable via some other mechanism... - JAnsi.install(launcher.topLoader) - try { - val main = appProvider.newMain() - try { withContextLoader(appProvider.loader)(main.run(appConfig)) } - catch { case e: xsbti.FullReload => if (e.clean) delete(launcher.bootDirectory); throw e } - } finally { - JAnsi.uninstall(launcher.topLoader) - } - } - final def launch(run: RunConfiguration => xsbti.MainResult)(config: RunConfiguration): Option[Int] = - { - run(config) match { - case e: xsbti.Exit => Some(e.code) - case c: xsbti.Continue => None - case r: xsbti.Reboot => launch(run)(new RunConfiguration(Option(r.scalaVersion), r.app, r.baseDirectory, r.arguments.toList)) - case x => throw new BootException("Invalid main result: " + x + (if (x eq null) "" else " (class: " + x.getClass + ")")) - } - } - private[this] def withContextLoader[T](loader: ClassLoader)(eval: => T): T = - { - val oldLoader = Thread.currentThread.getContextClassLoader - Thread.currentThread.setContextClassLoader(loader) - try { eval } finally { Thread.currentThread.setContextClassLoader(oldLoader) } - } - - // Cache of classes for lookup later. - val ServerMainClass = classOf[xsbti.ServerMain] - val AppMainClass = classOf[xsbti.AppMain] -} -final class RunConfiguration(val scalaVersion: Option[String], val app: xsbti.ApplicationID, val workingDirectory: File, val arguments: List[String]) - -import BootConfiguration.{ appDirectoryName, baseDirectoryName, extractScalaVersion, ScalaDirectoryName, TestLoadScalaClasses, ScalaOrg } -class Launch private[xsbt] (val bootDirectory: File, val lockBoot: Boolean, val ivyOptions: IvyOptions) extends xsbti.Launcher { - import ivyOptions.{ checksums => checksumsList, classifiers, repositories } - bootDirectory.mkdirs - private val scalaProviders = new Cache[(String, String), String, xsbti.ScalaProvider]((x, y) => getScalaProvider(x._1, x._2, y)) - def getScala(version: String): xsbti.ScalaProvider = getScala(version, "") - def getScala(version: String, reason: String): xsbti.ScalaProvider = getScala(version, reason, ScalaOrg) - def getScala(version: String, reason: String, scalaOrg: String) = scalaProviders((scalaOrg, version), reason) - def app(id: xsbti.ApplicationID, version: String): xsbti.AppProvider = app(id, Option(version)) - def app(id: xsbti.ApplicationID, scalaVersion: Option[String]): xsbti.AppProvider = - getAppProvider(id, scalaVersion, false) - - val bootLoader = new BootFilteredLoader(getClass.getClassLoader) - val topLoader = if (isWindows && !isCygwin) jansiLoader(bootLoader) else bootLoader - - val updateLockFile = if (lockBoot) Some(new File(bootDirectory, "sbt.boot.lock")) else None - - def globalLock: xsbti.GlobalLock = Locks - def ivyHome = orNull(ivyOptions.ivyHome) - def ivyRepositories = (repositories: List[xsbti.Repository]).toArray - def appRepositories = ((repositories filterNot (_.bootOnly)): List[xsbti.Repository]).toArray - def isOverrideRepositories: Boolean = ivyOptions.isOverrideRepositories - def checksums = checksumsList.toArray[String] - - // JAnsi needs to be shared between Scala and the application so there aren't two competing versions - def jansiLoader(parent: ClassLoader): ClassLoader = - { - val id = AppID("org.fusesource.jansi", "jansi", JAnsiVersion, "", toArray(Nil), xsbti.CrossValue.Disabled, array()) - val configuration = makeConfiguration(ScalaOrg, None) - val jansiHome = appDirectory(new File(bootDirectory, baseDirectoryName(ScalaOrg, None)), id) - val module = appModule(id, None, false, "jansi") - def makeLoader(): ClassLoader = { - val urls = toURLs(wrapNull(jansiHome.listFiles(JarFilter))) - val loader = new URLClassLoader(urls, bootLoader) - checkLoader(loader, module, "org.fusesource.jansi.internal.WindowsSupport" :: Nil, loader) - } - val existingLoader = - if (jansiHome.exists) - try Some(makeLoader()) catch { case e: Exception => None } - else - None - existingLoader getOrElse { - update(module, "") - makeLoader() - } - } - def checkLoader[T](loader: ClassLoader, module: ModuleDefinition, testClasses: Seq[String], ifValid: T): T = - { - val missing = getMissing(loader, testClasses) - if (missing.isEmpty) - ifValid - else - module.retrieveCorrupt(missing) - } - - private[this] def makeConfiguration(scalaOrg: String, version: Option[String]): UpdateConfiguration = - new UpdateConfiguration(bootDirectory, ivyOptions.ivyHome, scalaOrg, version, repositories, checksumsList) - - final def getAppProvider(id: xsbti.ApplicationID, explicitScalaVersion: Option[String], forceAppUpdate: Boolean): xsbti.AppProvider = - locked(new Callable[xsbti.AppProvider] { def call = getAppProvider0(id, explicitScalaVersion, forceAppUpdate) }) - - @tailrec private[this] final def getAppProvider0(id: xsbti.ApplicationID, explicitScalaVersion: Option[String], forceAppUpdate: Boolean): xsbti.AppProvider = - { - val app = appModule(id, explicitScalaVersion, true, "app") - /** Replace the version of an ApplicationID with the given one, if set. */ - def resolveId(appVersion: Option[String], id: xsbti.ApplicationID) = appVersion map { v => - import id._ - AppID(groupID(), name(), v, mainClass(), mainComponents(), crossVersionedValue(), classpathExtra()) - } getOrElse id - val baseDirs = (resolvedVersion: Option[String]) => (base: File) => appBaseDirs(base, resolveId(resolvedVersion, id)) - def retrieve() = { - val (appv, sv) = update(app, "") - val scalaVersion = strictOr(explicitScalaVersion, sv) - new RetrievedModule(true, app, sv, appv, baseDirs(appv)(scalaHome(ScalaOrg, scalaVersion))) - } - val retrievedApp = - if (forceAppUpdate) - retrieve() - else - existing(app, ScalaOrg, explicitScalaVersion, baseDirs(None)) getOrElse retrieve() - - val scalaVersion = getOrError(strictOr(explicitScalaVersion, retrievedApp.detectedScalaVersion), "No Scala version specified or detected") - val scalaProvider = getScala(scalaVersion, "(for " + id.name + ")") - val resolvedId = resolveId(retrievedApp.resolvedAppVersion, id) - - val (missing, appProvider) = checkedAppProvider(resolvedId, retrievedApp, scalaProvider) - if (missing.isEmpty) - appProvider - else if (retrievedApp.fresh) - app.retrieveCorrupt(missing) - else - getAppProvider0(resolvedId, explicitScalaVersion, true) - } - def scalaHome(scalaOrg: String, scalaVersion: Option[String]): File = new File(bootDirectory, baseDirectoryName(scalaOrg, scalaVersion)) - def appHome(id: xsbti.ApplicationID, scalaVersion: Option[String]): File = appDirectory(scalaHome(ScalaOrg, scalaVersion), id) - def checkedAppProvider(id: xsbti.ApplicationID, module: RetrievedModule, scalaProvider: xsbti.ScalaProvider): (Iterable[String], xsbti.AppProvider) = - { - val p = appProvider(id, module, scalaProvider, appHome(id, Some(scalaProvider.version))) - val missing = getMissing(p.loader, id.mainClass :: Nil) - (missing, p) - } - private[this] def locked[T](c: Callable[T]): T = Locks(orNull(updateLockFile), c) - def getScalaProvider(scalaOrg: String, scalaVersion: String, reason: String): xsbti.ScalaProvider = - locked(new Callable[xsbti.ScalaProvider] { def call = getScalaProvider0(scalaOrg, scalaVersion, reason) }) - - private[this] final def getScalaProvider0(scalaOrg: String, scalaVersion: String, reason: String) = - { - val scalaM = scalaModule(scalaOrg, scalaVersion) - val (scalaHome, lib) = scalaDirs(scalaM, scalaOrg, scalaVersion) - val baseDirs = lib :: Nil - def provider(retrieved: RetrievedModule): xsbti.ScalaProvider = { - val p = scalaProvider(scalaVersion, retrieved, topLoader, lib) - checkLoader(p.loader, retrieved.definition, TestLoadScalaClasses, p) - } - existing(scalaM, scalaOrg, Some(scalaVersion), _ => baseDirs) flatMap { mod => - try Some(provider(mod)) - catch { case e: Exception => None } - } getOrElse { - val (_, scalaVersion) = update(scalaM, reason) - provider(new RetrievedModule(true, scalaM, scalaVersion, baseDirs)) - } - } - - def existing(module: ModuleDefinition, scalaOrg: String, explicitScalaVersion: Option[String], baseDirs: File => List[File]): Option[RetrievedModule] = - { - val filter = new java.io.FileFilter { - val explicitName = explicitScalaVersion.map(sv => baseDirectoryName(scalaOrg, Some(sv))) - def accept(file: File) = file.isDirectory && explicitName.forall(_ == file.getName) - } - val retrieved = wrapNull(bootDirectory.listFiles(filter)) flatMap { scalaDir => - val appDir = directory(scalaDir, module.target) - if (appDir.exists) - new RetrievedModule(false, module, extractScalaVersion(scalaDir), baseDirs(scalaDir)) :: Nil - else - Nil - } - retrieved.headOption - } - def directory(scalaDir: File, target: UpdateTarget): File = target match { - case _: UpdateScala => scalaDir - case ua: UpdateApp => appDirectory(scalaDir, ua.id.toID) - } - def appBaseDirs(scalaHome: File, id: xsbti.ApplicationID): List[File] = - { - val appHome = appDirectory(scalaHome, id) - val components = componentProvider(appHome) - appHome :: id.mainComponents.map(components.componentLocation).toList - } - def appDirectory(base: File, id: xsbti.ApplicationID): File = - new File(base, appDirectoryName(id, File.separator)) - - def scalaDirs(module: ModuleDefinition, scalaOrg: String, scalaVersion: String): (File, File) = - { - val scalaHome = new File(bootDirectory, baseDirectoryName(scalaOrg, Some(scalaVersion))) - val libDirectory = new File(scalaHome, ScalaDirectoryName) - (scalaHome, libDirectory) - } - - def appProvider(appID: xsbti.ApplicationID, app: RetrievedModule, scalaProvider0: xsbti.ScalaProvider, appHome: File): xsbti.AppProvider = - new xsbti.AppProvider { - import Launch.{ ServerMainClass, AppMainClass } - val scalaProvider = scalaProvider0 - val id = appID - def mainClasspath = app.fullClasspath - lazy val loader = app.createLoader(scalaProvider.loader) - // TODO - For some reason we can't call this from vanilla scala. We get a - // no such method exception UNLESS we're in the same project. - lazy val entryPoint: Class[T] forSome { type T } = - { - val c = Class.forName(id.mainClass, true, loader) - if (classOf[xsbti.AppMain].isAssignableFrom(c)) c - else if (PlainApplication.isPlainApplication(c)) c - else if (ServerApplication.isServerApplication(c)) c - else sys.error(s"${c} is not an instance of xsbti.AppMain, xsbti.ServerMain nor does it have one of these static methods:\n" + - " * void main(String[] args)\n * int main(String[] args)\n * xsbti.Exit main(String[] args)\n") - } - // Deprecated API. Remove when we can. - def mainClass: Class[T] forSome { type T <: xsbti.AppMain } = entryPoint.asSubclass(AppMainClass) - def newMain(): xsbti.AppMain = { - if (ServerApplication.isServerApplication(entryPoint)) ServerApplication(this) - else if (PlainApplication.isPlainApplication(entryPoint)) PlainApplication(entryPoint) - else if (AppMainClass.isAssignableFrom(entryPoint)) mainClass.newInstance - else throw new IncompatibleClassChangeError(s"Main class ${entryPoint.getName} is not an instance of xsbti.AppMain, xsbti.ServerMain nor does it have a valid `main` method.") - } - lazy val components = componentProvider(appHome) - } - def componentProvider(appHome: File) = new ComponentProvider(appHome, lockBoot) - - def scalaProvider(scalaVersion: String, module: RetrievedModule, parentLoader: ClassLoader, scalaLibDir: File): xsbti.ScalaProvider = new xsbti.ScalaProvider { - def launcher = Launch.this - def version = scalaVersion - lazy val loader = module.createLoader(parentLoader) - - def compilerJar = new File(scalaLibDir, CompilerModuleName + ".jar") - def libraryJar = new File(scalaLibDir, LibraryModuleName + ".jar") - def jars = module.fullClasspath - - def app(id: xsbti.ApplicationID) = Launch.this.app(id, Some(scalaVersion)) - } - - def appModule(id: xsbti.ApplicationID, scalaVersion: Option[String], getClassifiers: Boolean, tpe: String): ModuleDefinition = new ModuleDefinition( - configuration = makeConfiguration(ScalaOrg, scalaVersion), - target = new UpdateApp(Application(id), if (getClassifiers) Value.get(classifiers.app) else Nil, tpe), - failLabel = id.name + " " + id.version, - extraClasspath = id.classpathExtra - ) - def scalaModule(org: String, version: String): ModuleDefinition = new ModuleDefinition( - configuration = makeConfiguration(org, Some(version)), - target = new UpdateScala(Value.get(classifiers.forScala)), - failLabel = "Scala " + version, - extraClasspath = array() - ) - /** Returns the resolved appVersion (if this was an App), as well as the scalaVersion. */ - def update(mm: ModuleDefinition, reason: String): (Option[String], Option[String]) = - { - val result = (new Update(mm.configuration))(mm.target, reason) - if (result.success) result.appVersion -> result.scalaVersion else mm.retrieveFailed - } -} -object Launcher { - def apply(bootDirectory: File, repositories: List[Repository.Repository]): xsbti.Launcher = - apply(bootDirectory, IvyOptions(None, Classifiers(Nil, Nil), repositories, BootConfiguration.DefaultChecksums, false)) - 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, true, ivyOptions) { - override def globalLock = locks - } - def apply(explicit: LaunchConfiguration): xsbti.Launcher = - new Launch(explicit.boot.directory, explicit.boot.lock, explicit.ivyConfiguration) - def defaultAppProvider(baseDirectory: File): xsbti.AppProvider = getAppProvider(baseDirectory, Configuration.configurationOnClasspath) - def getAppProvider(baseDirectory: File, configLocation: URL): xsbti.AppProvider = - { - val parsed = ResolvePaths(Configuration.parse(configLocation, baseDirectory), baseDirectory) - Initialize.process(parsed.boot.properties, parsed.appProperties, Initialize.selectQuick) - val config = ResolveValues(parsed) - val launcher = apply(config) - launcher.app(config.app.toID, orNull(config.getScalaVersion)) - } -} -class ComponentProvider(baseDirectory: File, lockBoot: Boolean) extends xsbti.ComponentProvider { - def componentLocation(id: String): File = new File(baseDirectory, id) - def component(id: String) = wrapNull(componentLocation(id).listFiles).filter(_.isFile) - def defineComponent(id: String, files: Array[File]) = - { - val location = componentLocation(id) - if (location.exists) - throw new BootException("Cannot redefine component. ID: " + id + ", files: " + files.mkString(",")) - else - Copy(files.toList, location) - } - def addToComponent(id: String, files: Array[File]): Boolean = - Copy(files.toList, componentLocation(id)) - def lockFile = if (lockBoot) ComponentProvider.lockFile(baseDirectory) else null // null for the Java interface -} -object ComponentProvider { - def lockFile(baseDirectory: File) = - { - baseDirectory.mkdirs() - new File(baseDirectory, "sbt.components.lock") - } -} diff --git a/launch/src/main/scala/xsbt/boot/LaunchConfiguration.scala b/launch/src/main/scala/xsbt/boot/LaunchConfiguration.scala deleted file mode 100644 index f7fce64a8..000000000 --- a/launch/src/main/scala/xsbt/boot/LaunchConfiguration.scala +++ /dev/null @@ -1,154 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.io.File -import java.net.URL -import scala.collection.immutable.List - -//TODO: use copy constructor, check size change -final case class LaunchConfiguration(scalaVersion: Value[String], ivyConfiguration: IvyOptions, app: Application, boot: BootSetup, logging: Logging, appProperties: List[AppProperty], serverConfig: Option[ServerConfiguration]) { - def isServer: Boolean = serverConfig.isDefined - def getScalaVersion = { - val sv = Value.get(scalaVersion) - if (sv == "auto") None else Some(sv) - } - - def withScalaVersion(newScalaVersion: String) = LaunchConfiguration(new Explicit(newScalaVersion), ivyConfiguration, app, boot, logging, appProperties, serverConfig) - def withApp(app: Application) = LaunchConfiguration(scalaVersion, ivyConfiguration, app, boot, logging, appProperties, serverConfig) - def withAppVersion(newAppVersion: String) = LaunchConfiguration(scalaVersion, ivyConfiguration, app.withVersion(new Explicit(newAppVersion)), boot, logging, appProperties, serverConfig) - // TODO: withExplicit - def withVersions(newScalaVersion: String, newAppVersion: String, classifiers0: Classifiers) = - LaunchConfiguration(new Explicit(newScalaVersion), ivyConfiguration.copy(classifiers = classifiers0), app.withVersion(new Explicit(newAppVersion)), boot, logging, appProperties, serverConfig) - - def map(f: File => File) = LaunchConfiguration(scalaVersion, ivyConfiguration.map(f), app.map(f), boot.map(f), logging, appProperties, serverConfig.map(_ map f)) -} -object LaunchConfiguration { - // Saves a launch configuration into a file. This is only safe if it is loaded by the *same* launcher version. - def save(config: LaunchConfiguration, f: File): Unit = { - val out = new java.io.ObjectOutputStream(new java.io.FileOutputStream(f)) - try out.writeObject(config) - finally out.close() - } - // Restores a launch configuration from a file. This is only safe if it is loaded by the *same* launcher version. - def restore(url: URL): LaunchConfiguration = { - val in = new java.io.ObjectInputStream(url.openConnection.getInputStream) - try in.readObject.asInstanceOf[LaunchConfiguration] - finally in.close() - } -} -final case class ServerConfiguration(lockFile: File, jvmArgs: Option[File], jvmPropsFile: Option[File]) { - def map(f: File => File) = - ServerConfiguration(f(lockFile), jvmArgs map f, jvmPropsFile map f) -} -final case class IvyOptions(ivyHome: Option[File], classifiers: Classifiers, repositories: List[Repository.Repository], checksums: List[String], isOverrideRepositories: Boolean) { - def map(f: File => File) = IvyOptions(ivyHome.map(f), classifiers, repositories, checksums, isOverrideRepositories) -} -sealed trait Value[T] extends Serializable -final class Explicit[T](val value: T) extends Value[T] { - override def toString = value.toString -} -final class Implicit[T](val name: String, val default: Option[T]) extends Value[T] { - require(isNonEmpty(name), "Name cannot be empty") - override def toString = name + (default match { case Some(d) => "[" + d + "]"; case None => "" }) -} -object Value { - def get[T](v: Value[T]): T = v match { case e: Explicit[T] => e.value; case _ => throw new BootException("Unresolved version: " + v) } - def readImplied[T](s: String, name: String, default: Option[String])(implicit read: String => T): Value[T] = - if (s == "read") new Implicit(name, default map read) else Pre.error("Expected 'read', got '" + s + "'") -} - -final case class Classifiers(forScala: Value[List[String]], app: Value[List[String]]) -object Classifiers { - def apply(forScala: List[String], app: List[String]): Classifiers = Classifiers(new Explicit(forScala), new Explicit(app)) -} - -object LaunchCrossVersion { - def apply(s: String): xsbti.CrossValue = - s match { - case x if CrossVersionUtil.isFull(s) => xsbti.CrossValue.Full - case x if CrossVersionUtil.isBinary(s) => xsbti.CrossValue.Binary - case x if CrossVersionUtil.isDisabled(s) => xsbti.CrossValue.Disabled - case x => Pre.error("Unknown value '" + x + "' for property 'cross-versioned'") - } -} - -final case class Application(groupID: String, name: String, version: Value[String], main: String, components: List[String], crossVersioned: xsbti.CrossValue, classpathExtra: Array[File]) { - def getVersion = Value.get(version) - def withVersion(newVersion: Value[String]) = Application(groupID, name, newVersion, main, components, crossVersioned, classpathExtra) - def toID = AppID(groupID, name, getVersion, main, toArray(components), crossVersioned, classpathExtra) - def map(f: File => File) = Application(groupID, name, version, main, components, crossVersioned, classpathExtra.map(f)) -} -final case class AppID(groupID: String, name: String, version: String, mainClass: String, mainComponents: Array[String], crossVersionedValue: xsbti.CrossValue, classpathExtra: Array[File]) extends xsbti.ApplicationID { - def crossVersioned: Boolean = crossVersionedValue != xsbti.CrossValue.Disabled -} - -object Application { - def apply(id: xsbti.ApplicationID): Application = - { - import id._ - Application(groupID, name, new Explicit(version), mainClass, mainComponents.toList, safeCrossVersionedValue(id), classpathExtra) - } - - private def safeCrossVersionedValue(id: xsbti.ApplicationID): xsbti.CrossValue = - try id.crossVersionedValue - catch { - case _: AbstractMethodError => - // Before 0.13 this method did not exist on application, so we need to provide a default value - //in the event we're dealing with an older Application. - if (id.crossVersioned) xsbti.CrossValue.Binary - else xsbti.CrossValue.Disabled - } -} - -object Repository { - trait Repository extends xsbti.Repository { - def bootOnly: Boolean - } - final case class Maven(id: String, url: URL, bootOnly: Boolean = false) extends xsbti.MavenRepository with Repository - final case class Ivy(id: String, url: URL, ivyPattern: String, artifactPattern: String, mavenCompatible: Boolean, bootOnly: Boolean = false, descriptorOptional: Boolean = false, skipConsistencyCheck: Boolean = false) extends xsbti.IvyRepository with Repository - final case class Predefined(id: xsbti.Predefined, bootOnly: Boolean = false) extends xsbti.PredefinedRepository with Repository - object Predefined { - def apply(s: String): Predefined = new Predefined(xsbti.Predefined.toValue(s), false) - def apply(s: String, bootOnly: Boolean): Predefined = new Predefined(xsbti.Predefined.toValue(s), bootOnly) - } - - def isMavenLocal(repo: xsbti.Repository) = repo match { case p: xsbti.PredefinedRepository => p.id == xsbti.Predefined.MavenLocal; case _ => false } - def defaults: List[xsbti.Repository] = xsbti.Predefined.values.map(x => Predefined(x, false)).toList -} - -final case class Search(tpe: Search.Value, paths: List[File]) -object Search extends Enumeration { - def none = Search(Current, Nil) - val Only = value("only") - val RootFirst = value("root-first") - val Nearest = value("nearest") - val Current = value("none") - def apply(s: String, paths: List[File]): Search = Search(toValue(s), paths) -} - -final case class BootSetup(directory: File, lock: Boolean, properties: File, search: Search, promptCreate: String, enableQuick: Boolean, promptFill: Boolean) { - def map(f: File => File) = BootSetup(f(directory), lock, f(properties), search, promptCreate, enableQuick, promptFill) -} -final case class AppProperty(name: String)(val quick: Option[PropertyInit], val create: Option[PropertyInit], val fill: Option[PropertyInit]) - -sealed trait PropertyInit -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 Serializable { - def log(s: => String, at: LogLevel.Value) = if (level.id <= at.id) stream(at).println("[" + at + "] " + s) - def debug(s: => String) = log(s, LogLevel.Debug) - private def stream(at: LogLevel.Value) = if (at == LogLevel.Error) System.err else System.out -} -object LogLevel extends Enumeration { - 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)) -} - -final class AppConfiguration(val arguments: Array[String], val baseDirectory: File, val provider: xsbti.AppProvider) extends xsbti.AppConfiguration diff --git a/launch/src/main/scala/xsbt/boot/ListMap.scala b/launch/src/main/scala/xsbt/boot/ListMap.scala deleted file mode 100644 index 6dcd679d0..000000000 --- a/launch/src/main/scala/xsbt/boot/ListMap.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import scala.collection.{ Iterable, Iterator } -import scala.collection.immutable.List - -// preserves iteration order -sealed class ListMap[K, V] private (backing: List[(K, V)]) extends Iterable[(K, V)] // use Iterable because Traversable.toStream loops -{ - import ListMap.remove - def update(k: K, v: V) = this.+((k, v)) - def +(pair: (K, V)) = copy(pair :: remove(backing, pair._1)) - def -(k: K) = copy(remove(backing, k)) - def get(k: K): Option[V] = backing.find(_._1 == k).map(_._2) - def keys: List[K] = backing.reverse.map(_._1) - def apply(k: K): V = getOrError(get(k), "Key " + k + " not found") - def contains(k: K): Boolean = get(k).isDefined - def iterator = backing.reverse.iterator - override def isEmpty: Boolean = backing.isEmpty - override def toList = backing.reverse - override def toSeq = toList - protected def copy(newBacking: List[(K, V)]): ListMap[K, V] = new ListMap(newBacking) - def default(defaultF: K => V): ListMap[K, V] = - new ListMap[K, V](backing) { - override def apply(k: K) = super.get(k).getOrElse(defaultF(k)) - override def copy(newBacking: List[(K, V)]) = super.copy(newBacking).default(defaultF) - } - override def toString = backing.mkString("ListMap(", ",", ")") -} -object ListMap { - def apply[K, V](pairs: (K, V)*) = new ListMap[K, V](pairs.toList.distinct) - def empty[K, V] = new ListMap[K, V](Nil) - private def remove[K, V](backing: List[(K, V)], k: K) = backing.filter(_._1 != k) -} diff --git a/launch/src/main/scala/xsbt/boot/Locks.scala b/launch/src/main/scala/xsbt/boot/Locks.scala deleted file mode 100644 index f53abf721..000000000 --- a/launch/src/main/scala/xsbt/boot/Locks.scala +++ /dev/null @@ -1,101 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import java.io.{ File, FileOutputStream, IOException } -import java.nio.channels.FileChannel -import java.util.concurrent.Callable -import scala.collection.immutable.List -import scala.annotation.tailrec - -object GetLocks { - /** - * Searches for Locks in parent class loaders before returning Locks from this class loader. - * Normal class loading doesn't work because the launcher class loader hides xsbt classes. - */ - def find: xsbti.GlobalLock = - Loaders(getClass.getClassLoader.getParent).flatMap(tryGet).headOption.getOrElse(Locks) - private[this] def tryGet(loader: ClassLoader): List[xsbti.GlobalLock] = - try { getLocks0(loader) :: Nil } catch { case e: ClassNotFoundException => Nil } - private[this] def getLocks0(loader: ClassLoader) = - Class.forName("xsbt.boot.Locks$", true, loader).getField("MODULE$").get(null).asInstanceOf[xsbti.GlobalLock] -} - -// gets a file lock by first getting a JVM-wide lock. -object Locks extends xsbti.GlobalLock { - private[this] val locks = new Cache[File, Unit, GlobalLock]((f, _) => new GlobalLock(f)) - def apply[T](file: File, action: Callable[T]): T = if (file eq null) action.call else apply0(file, action) - private[this] def apply0[T](file: File, action: Callable[T]): T = - { - val lock = - synchronized { - file.getParentFile.mkdirs() - file.createNewFile() - locks(file.getCanonicalFile, ()) - } - lock.withLock(action) - } - - private[this] class GlobalLock(file: File) { - private[this] var fileLocked = false - def withLock[T](run: Callable[T]): T = - synchronized { - if (fileLocked) - run.call - else { - fileLocked = true - try { ignoringDeadlockAvoided(run) } - finally { fileLocked = false } - } - } - - // https://github.com/sbt/sbt/issues/650 - // This approach means a real deadlock won't be detected - @tailrec private[this] def ignoringDeadlockAvoided[T](run: Callable[T]): T = - { - val result = - try { Some(withFileLock(run)) } - catch { - case i: IOException if isDeadlockAvoided(i) => - // there should be a timeout to the deadlock avoidance, so this is just a backup - Thread.sleep(200) - None - } - result match { // workaround for no tailrec optimization in the above try/catch - case Some(t) => t - case None => ignoringDeadlockAvoided(run) - } - } - - // The actual message is not specified by FileChannel.lock, so this may need to be adjusted for different JVMs - private[this] def isDeadlockAvoided(i: IOException): Boolean = - i.getMessage == "Resource deadlock avoided" - - private[this] def withFileLock[T](run: Callable[T]): T = - { - def withChannelRetries(retries: Int)(channel: FileChannel): T = - try { withChannel(channel) } - catch { - case i: InternalLockNPE => - if (retries > 0) withChannelRetries(retries - 1)(channel) else throw i - } - - def withChannel(channel: FileChannel) = - { - val freeLock = try { channel.tryLock } catch { case e: NullPointerException => throw new InternalLockNPE(e) } - if (freeLock eq null) { - System.out.println("Waiting for lock on " + file + " to be available..."); - val lock = try { channel.lock } catch { case e: NullPointerException => throw new InternalLockNPE(e) } - try { run.call } - finally { lock.release() } - } else { - try { run.call } - finally { freeLock.release() } - } - } - Using(new FileOutputStream(file).getChannel)(withChannelRetries(5)) - } - } - private[this] final class InternalLockNPE(cause: Exception) extends RuntimeException(cause) -} diff --git a/launch/src/main/scala/xsbt/boot/ModuleDefinition.scala b/launch/src/main/scala/xsbt/boot/ModuleDefinition.scala deleted file mode 100644 index bf0489fd4..000000000 --- a/launch/src/main/scala/xsbt/boot/ModuleDefinition.scala +++ /dev/null @@ -1,25 +0,0 @@ -package xsbt.boot - -import Pre._ -import java.io.File -import java.net.URLClassLoader - -final class ModuleDefinition(val configuration: UpdateConfiguration, val extraClasspath: Array[File], val target: UpdateTarget, val failLabel: String) { - def retrieveFailed: Nothing = fail("") - def retrieveCorrupt(missing: Iterable[String]): Nothing = fail(": missing " + missing.mkString(", ")) - private def fail(extra: String) = - throw new xsbti.RetrieveException(versionString, "Could not retrieve " + failLabel + extra) - private def versionString: String = target match { case _: UpdateScala => configuration.getScalaVersion; case a: UpdateApp => Value.get(a.id.version) } -} - -final class RetrievedModule(val fresh: Boolean, val definition: ModuleDefinition, val detectedScalaVersion: Option[String], val resolvedAppVersion: Option[String], val baseDirectories: List[File]) { - /** Use this constructor only when the module exists already, or when its version is not dynamic (so its resolved version would be the same) */ - def this(fresh: Boolean, definition: ModuleDefinition, detectedScalaVersion: Option[String], baseDirectories: List[File]) = - this(fresh, definition, detectedScalaVersion, None, baseDirectories) - - lazy val classpath: Array[File] = getJars(baseDirectories) - lazy val fullClasspath: Array[File] = concat(classpath, definition.extraClasspath) - - def createLoader(parentLoader: ClassLoader): ClassLoader = - new URLClassLoader(toURLs(fullClasspath), parentLoader) -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/PlainApplication.scala b/launch/src/main/scala/xsbt/boot/PlainApplication.scala deleted file mode 100644 index 93658e098..000000000 --- a/launch/src/main/scala/xsbt/boot/PlainApplication.scala +++ /dev/null @@ -1,48 +0,0 @@ -package xsbt -package boot - -/** A wrapper around 'raw' static methods to meet the sbt application interface. */ -class PlainApplication private (mainMethod: java.lang.reflect.Method) extends xsbti.AppMain { - override def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = { - // TODO - Figure out if the main method returns an Int... - val IntClass = classOf[Int] - val ExitClass = classOf[xsbti.Exit] - // It seems we may need to wrap exceptions here... - try mainMethod.getReturnType match { - case ExitClass => - mainMethod.invoke(null, configuration.arguments).asInstanceOf[xsbti.Exit] - case IntClass => - PlainApplication.Exit(mainMethod.invoke(null, configuration.arguments).asInstanceOf[Int]) - case _ => - // Here we still invoke, but return 0 if sucessful (no exceptions). - mainMethod.invoke(null, configuration.arguments) - PlainApplication.Exit(0) - } catch { - // This is only thrown if the underlying reflective call throws. - // Let's expose the underlying error. - case e: java.lang.reflect.InvocationTargetException if e.getCause != null => - throw e.getCause - } - - } -} -/** An object that lets us detect compatible "plain" applications and launch them reflectively. */ -object PlainApplication { - def isPlainApplication(clazz: Class[_]): Boolean = findMainMethod(clazz).isDefined - def apply(clazz: Class[_]): xsbti.AppMain = - findMainMethod(clazz) match { - case Some(method) => new PlainApplication(method) - case None => sys.error("Class: " + clazz + " does not have a main method!") - } - private def findMainMethod(clazz: Class[_]): Option[java.lang.reflect.Method] = - try { - val method = - clazz.getMethod("main", classOf[Array[String]]) - if (java.lang.reflect.Modifier.isStatic(method.getModifiers)) Some(method) - else None - } catch { - case n: NoSuchMethodException => None - } - - case class Exit(code: Int) extends xsbti.Exit -} diff --git a/launch/src/main/scala/xsbt/boot/Pre.scala b/launch/src/main/scala/xsbt/boot/Pre.scala deleted file mode 100644 index cc5abbad3..000000000 --- a/launch/src/main/scala/xsbt/boot/Pre.scala +++ /dev/null @@ -1,104 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah - */ -package xsbt.boot - -import scala.collection.immutable.List -import java.io.{ File, FileFilter } -import java.net.{ URL, URLClassLoader } -import java.util.Locale - -object Pre { - def readLine(prompt: String): Option[String] = { - val c = System.console() - if (c eq null) None else Option(c.readLine(prompt)) - } - def trimLeading(line: String) = - { - def newStart(i: Int): Int = if (i >= line.length || !Character.isWhitespace(line.charAt(i))) i else newStart(i + 1) - line.substring(newStart(0)) - } - def isEmpty(line: String) = line.length == 0 - def isNonEmpty(line: String) = line.length > 0 - def assert(condition: Boolean, msg: => String): Unit = if (!condition) throw new AssertionError(msg) - def assert(condition: Boolean): Unit = assert(condition, "Assertion failed") - def require(condition: Boolean, msg: => String): Unit = if (!condition) throw new IllegalArgumentException(msg) - def error(msg: String): Nothing = throw new BootException(prefixError(msg)) - def declined(msg: String): Nothing = throw new BootException(msg) - def prefixError(msg: String): String = "Error during sbt execution: " + msg - def toBoolean(s: String) = java.lang.Boolean.parseBoolean(s) - def toArray[T: ClassManifest](list: List[T]) = - { - val arr = new Array[T](list.length) - def copy(i: Int, rem: List[T]): Unit = - if (i < arr.length) { - arr(i) = rem.head - copy(i + 1, rem.tail) - } - copy(0, list) - arr - } - /* These exist in order to avoid bringing in dependencies on RichInt and ArrayBuffer, among others. */ - def concat(a: Array[File], b: Array[File]): Array[File] = - { - val n = new Array[File](a.length + b.length) - java.lang.System.arraycopy(a, 0, n, 0, a.length) - java.lang.System.arraycopy(b, 0, n, a.length, b.length) - n - } - def array(files: File*): Array[File] = toArray(files.toList) - /* Saves creating a closure for default if it has already been evaluated*/ - def orElse[T](opt: Option[T], default: T) = if (opt.isDefined) opt.get else default - - def wrapNull(a: Array[File]): Array[File] = if (a == null) new Array[File](0) else a - def const[B](b: B): Any => B = _ => b - def strictOr[T](a: Option[T], b: Option[T]): Option[T] = a match { case None => b; case _ => a } - def getOrError[T](a: Option[T], msg: String): T = a match { case None => error(msg); case Some(x) => x } - def orNull[T >: Null](t: Option[T]): T = t match { case None => null; case Some(x) => x } - - def getJars(directories: List[File]): Array[File] = toArray(directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter)))) - - object JarFilter extends FileFilter { - def accept(file: File) = !file.isDirectory && file.getName.endsWith(".jar") - } - def getMissing(loader: ClassLoader, classes: Iterable[String]): Iterable[String] = - { - def classMissing(c: String) = try { Class.forName(c, false, loader); false } catch { case e: ClassNotFoundException => true } - classes.toList.filter(classMissing) - } - def toURLs(files: Array[File]): Array[URL] = files.map(_.toURI.toURL) - def toFile(url: URL): File = - try { new File(url.toURI) } - catch { case _: java.net.URISyntaxException => new File(url.getPath) } - - def delete(f: File) { - if (f.isDirectory) { - val fs = f.listFiles() - if (fs ne null) fs foreach delete - } - if (f.exists) f.delete() - } - final val isWindows: Boolean = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows") - final val isCygwin: Boolean = isWindows && java.lang.Boolean.getBoolean("sbt.cygwin") - - import java.util.Properties - import java.io.{ FileInputStream, FileOutputStream } - private[boot] def readProperties(propertiesFile: File) = - { - val properties = new Properties - if (propertiesFile.exists) - Using(new FileInputStream(propertiesFile))(properties.load) - properties - } - private[boot] def writeProperties(properties: Properties, file: File, msg: String): Unit = { - file.getParentFile.mkdirs() - Using(new FileOutputStream(file))(out => properties.store(out, msg)) - } - private[boot] def setSystemProperties(properties: Properties): Unit = { - val nameItr = properties.stringPropertyNames.iterator - while (nameItr.hasNext) { - val propName = nameItr.next - System.setProperty(propName, properties.getProperty(propName)) - } - } -} diff --git a/launch/src/main/scala/xsbt/boot/ResolveValues.scala b/launch/src/main/scala/xsbt/boot/ResolveValues.scala deleted file mode 100644 index 0e69d0194..000000000 --- a/launch/src/main/scala/xsbt/boot/ResolveValues.scala +++ /dev/null @@ -1,43 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.io.{ File, FileInputStream } -import java.util.Properties - -object ResolveValues { - def apply(conf: LaunchConfiguration): LaunchConfiguration = (new ResolveValues(conf))() - private def trim(s: String) = if (s eq null) None else notEmpty(s.trim) - private def notEmpty(s: String) = if (isEmpty(s)) None else Some(s) -} - -import ResolveValues.trim -final class ResolveValues(conf: LaunchConfiguration) { - private def propertiesFile = conf.boot.properties - private lazy val properties = readProperties(propertiesFile) - def apply(): LaunchConfiguration = - { - import conf._ - val scalaVersion = resolve(conf.scalaVersion) - val appVersion = resolve(app.version) - val classifiers = resolveClassifiers(ivyConfiguration.classifiers) - withVersions(scalaVersion, appVersion, classifiers) - } - def resolveClassifiers(classifiers: Classifiers): Classifiers = - { - import ConfigurationParser.readIDs - // the added "" ensures that the main jars are retrieved - val scalaClassifiers = "" :: resolve(classifiers.forScala) - val appClassifiers = "" :: resolve(classifiers.app) - Classifiers(new Explicit(scalaClassifiers), new Explicit(appClassifiers)) - } - def resolve[T](v: Value[T])(implicit read: String => T): T = - v match { - case e: Explicit[t] => e.value - case i: Implicit[t] => - trim(properties.getProperty(i.name)) map read orElse - i.default getOrElse ("No " + i.name + " specified in " + propertiesFile) - } -} \ No newline at end of file diff --git a/launch/src/main/scala/xsbt/boot/ServerApplication.scala b/launch/src/main/scala/xsbt/boot/ServerApplication.scala deleted file mode 100644 index 8ac87c4d7..000000000 --- a/launch/src/main/scala/xsbt/boot/ServerApplication.scala +++ /dev/null @@ -1,291 +0,0 @@ -package xsbt -package boot - -import java.io.File -import scala.util.control.NonFatal -import java.net.URI -import java.io.IOException -import Pre._ -import scala.annotation.tailrec - -/** A wrapper around 'raw' static methods to meet the sbt application interface. */ -class ServerApplication private (provider: xsbti.AppProvider) extends xsbti.AppMain { - import ServerApplication._ - - override def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = { - val serverMain = provider.entryPoint.asSubclass(ServerMainClass).newInstance - val server = serverMain.start(configuration) - System.out.println(s"${SERVER_SYNCH_TEXT}${server.uri}") - server.awaitTermination() - } -} -/** An object that lets us detect compatible "plain" applications and launch them reflectively. */ -object ServerApplication { - val SERVER_SYNCH_TEXT = "[SERVER-URI]" - val ServerMainClass = classOf[xsbti.ServerMain] - // TODO - We should also adapt friendly static methods into servers, perhaps... - // We could even structurally type things that have a uri + awaitTermination method... - def isServerApplication(clazz: Class[_]): Boolean = - ServerMainClass.isAssignableFrom(clazz) - def apply(provider: xsbti.AppProvider): xsbti.AppMain = - new ServerApplication(provider) - -} -object ServerLocator { - // TODO - Probably want to drop this to reduce classfile size - private def locked[U](file: File)(f: => U): U = { - Locks(file, new java.util.concurrent.Callable[U] { - def call(): U = f - }) - } - // We use the lock file they give us to write the server info. However, - // it seems we cannot both use the server info file for locking *and* - // read from it successfully. Locking seems to blank the file. SO, we create - // another file near the info file to lock.a - def makeLockFile(f: File): File = - new File(f.getParentFile, s"${f.getName}.lock") - // Launch the process and read the port... - def locate(currentDirectory: File, config: LaunchConfiguration): URI = - config.serverConfig match { - case None => sys.error("No server lock file configured. Cannot locate server.") - case Some(sc) => locked(makeLockFile(sc.lockFile)) { - readProperties(sc.lockFile) match { - case Some(uri) if isReachable(uri) => uri - case _ => - val uri = ServerLauncher.startServer(currentDirectory, config) - writeProperties(sc.lockFile, uri) - uri - } - } - } - - private val SERVER_URI_PROPERTY = "server.uri" - def readProperties(f: File): Option[java.net.URI] = { - try { - val props = Pre.readProperties(f) - props.getProperty(SERVER_URI_PROPERTY) match { - case null => None - case uri => Some(new java.net.URI(uri)) - } - } catch { - case e: IOException => None - } - } - def writeProperties(f: File, uri: URI): Unit = { - val props = new java.util.Properties - props.setProperty(SERVER_URI_PROPERTY, uri.toASCIIString) - val output = new java.io.FileOutputStream(f) - val df = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ") - df.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) - Pre.writeProperties(props, f, s"Server Startup at ${df.format(new java.util.Date)}") - } - - def isReachable(uri: java.net.URI): Boolean = - try { - // TODO - For now we assume if we can connect, it means - // that the server is working... - val socket = new java.net.Socket(uri.getHost, uri.getPort) - try socket.isConnected - finally socket.close() - } catch { - case e: IOException => false - } -} -/** A helper class that dumps incoming values into a print stream. */ -class StreamDumper(in: java.io.BufferedReader, out: java.io.PrintStream) extends Thread { - // Don't block the application for this thread. - setDaemon(true) - val endTime = new java.util.concurrent.atomic.AtomicLong(Long.MaxValue) - override def run(): Unit = { - def read(): Unit = if (endTime.get > System.currentTimeMillis) in.readLine match { - case null => () - case line => - out.println(line) - out.flush() - read() - } - read() - out.close() - } - - def close(waitForErrors: Boolean): Unit = { - // closing "in" blocks forever on Windows, so don't do it; - // just wait a couple seconds to read more stuff if there is - // any stuff. - if (waitForErrors) { - endTime.set(System.currentTimeMillis + 5000) - // at this point we'd rather the dumper thread run - // before we check whether to sleep - Thread.`yield`() - // let ourselves read more (thread should exit on earlier of endTime or EOF) - while (isAlive() && (endTime.get > System.currentTimeMillis)) - Thread.sleep(50) - } else { - endTime.set(System.currentTimeMillis) - } - } -} -object ServerLauncher { - import ServerApplication.SERVER_SYNCH_TEXT - def startServer(currentDirectory: File, config: LaunchConfiguration): URI = { - val serverConfig = config.serverConfig match { - case Some(c) => c - case None => throw new RuntimeException("Logic Failure: Attempting to start a server that isn't configured to be a server. Please report a bug.") - } - val launchConfig = java.io.File.createTempFile("sbtlaunch", "config") - if (System.getenv("SBT_SERVER_SAVE_TEMPS") eq null) - launchConfig.deleteOnExit() - LaunchConfiguration.save(config, launchConfig) - val jvmArgs: List[String] = serverJvmArgs(currentDirectory, serverConfig) - val cmd: List[String] = - ("java" :: jvmArgs) ++ - ("-jar" :: defaultLauncherLookup.getCanonicalPath :: s"@load:${launchConfig.toURI.toURL.toString}" :: Nil) - launchProcessAndGetUri(cmd, currentDirectory) - } - - // Here we try to isolate all the stupidity of dealing with Java processes. - def launchProcessAndGetUri(cmd: List[String], cwd: File): URI = { - // TODO - Handle windows path stupidity in arguments. - val pb = new java.lang.ProcessBuilder() - pb.command(cmd: _*) - pb.directory(cwd) - val process = pb.start() - // First we need to grab all the input streams, and close the ones we don't care about. - process.getOutputStream.close() - val stderr = process.getErrorStream - val stdout = process.getInputStream - // Now we start dumping out errors. - val errorDumper = new StreamDumper(new java.io.BufferedReader(new java.io.InputStreamReader(stderr)), System.err) - errorDumper.start() - // Now we look for the URI synch value, and then make sure we close the output files. - try readUntilSynch(new java.io.BufferedReader(new java.io.InputStreamReader(stdout))) match { - case Some(uri) => uri - case _ => - // attempt to get rid of the server (helps prevent hanging / stuck locks, - // though this is not reliable) - try process.destroy() catch { case e: Exception => } - // block a second to try to get stuff from stderr - errorDumper.close(waitForErrors = true) - sys.error(s"Failed to start server process in ${pb.directory} command line ${pb.command}") - } finally { - errorDumper.close(waitForErrors = false) - stdout.close() - // Do not close stderr here because on Windows that will block, - // and since the child process has no reason to exit, it may - // block forever. errorDumper.close() instead owns the problem - // of deciding what to do with stderr. - } - } - - object ServerUriLine { - def unapply(in: String): Option[URI] = - if (in startsWith SERVER_SYNCH_TEXT) { - Some(new URI(in.substring(SERVER_SYNCH_TEXT.size))) - } else None - } - /** Reads an input steam until it hits the server synch text and server URI. */ - def readUntilSynch(in: java.io.BufferedReader): Option[URI] = { - @tailrec - def read(): Option[URI] = in.readLine match { - case null => None - case ServerUriLine(uri) => Some(uri) - case line => read() - } - try read() - finally in.close() - } - /** Reads all the lines in a file. If it doesn't exist, returns an empty list. Forces UTF-8 strings. */ - def readLines(f: File): List[String] = - if (!f.exists) Nil else { - val reader = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(f), "UTF-8")) - @tailrec - def read(current: List[String]): List[String] = - reader.readLine match { - case null => current.reverse - case line => read(line :: current) - } - try read(Nil) - finally reader.close() - } - - // None = couldn't figure it out - def javaIsAbove(currentDirectory: File, version: Int): Option[Boolean] = try { - val pb = new java.lang.ProcessBuilder() - // hopefully "java -version" is a lot faster than booting the full JVM. - // not sure how else we can do this. - pb.command("java", "-version") - pb.directory(currentDirectory) - val process = pb.start() - try { - process.getOutputStream.close() - process.getInputStream.close() - val stderr = new java.io.LineNumberReader(new java.io.InputStreamReader(process.getErrorStream)) - // Looking for the first line which is `java version "1.7.0_60"` or similar - val lineOption = try Option(stderr.readLine()) finally stderr.close() - val pattern = java.util.regex.Pattern.compile("""java version "[0-9]+\.([0-9]+)\..*".*""") - lineOption flatMap { line => - val matcher = pattern.matcher(line) - if (matcher.matches()) { - try Some(Integer.parseInt(matcher.group(1)) > version) catch { case NonFatal(_) => None } - } else { - System.err.println(s"Failed to parse version from 'java -version' output '$line'") - None - } - } - } finally { - process.destroy() - try { process.waitFor() } catch { case NonFatal(_) => } - } - } catch { - case e: IOException => - // both process.start and reading the output streams can throw IOException. - // all OS exceptions from process.start are supposed to be IOException. - System.err.println(s"Failed to run 'java -version': ${e.getClass.getName}: ${e.getMessage}") - None - } - - def serverJvmArgs(currentDirectory: File, serverConfig: ServerConfiguration): List[String] = - serverJvmArgs(currentDirectory, serverConfig.jvmArgs map readLines getOrElse Nil) - - final val memOptPrefixes = List("-Xmx", "-Xms", "-XX:MaxPermSize", "-XX:PermSize", "-XX:ReservedCodeCacheSize", "-XX:MaxMetaspaceSize", "-XX:MetaspaceSize") - - final val defaultMinHeapM = 256 - final val defaultMaxHeapM = defaultMinHeapM * 4 - final val defaultMinPermM = 64 - final val defaultMaxPermM = defaultMinPermM * 4 - - // this is separate just for the test suite - def serverJvmArgs(currentDirectory: File, baseArgs: List[String]): List[String] = { - // ignore blank lines - val trimmed = baseArgs.map(_.trim).filterNot(_.isEmpty) - // If the user config has provided ANY memory options we bail out and do NOT add - // any defaults. This means people can always fix our mistakes, and it avoids - // issues where the JVM refuses to start because of (for example) min size greater - // than max size. We don't want to deal with coordinating our changes with the - // user configuration. - def isMemoryOption(s: String) = memOptPrefixes.exists(s.startsWith(_)) - if (trimmed.exists(isMemoryOption(_))) - trimmed - else { - val permOptions = javaIsAbove(currentDirectory, 7) match { - case Some(true) => List(s"-XX:MetaspaceSize=${defaultMinPermM}m", s"-XX:MaxMetaspaceSize=${defaultMaxPermM}m") - case Some(false) => List(s"-XX:PermSize=${defaultMinPermM}m", s"-XX:MaxPermSize=${defaultMaxPermM}m") - case None => Nil // don't know what we're doing, so don't set options - } - s"-Xms${defaultMinHeapM}m" :: s"-Xmx${defaultMaxHeapM}m" :: (permOptions ++ trimmed) - } - } - - def defaultLauncherLookup: File = - try { - val classInLauncher = classOf[AppConfiguration] - val fileOpt = for { - domain <- Option(classInLauncher.getProtectionDomain) - source <- Option(domain.getCodeSource) - location = source.getLocation - } yield toFile(location) - fileOpt.getOrElse(throw new RuntimeException("Could not inspect protection domain or code source")) - } catch { - case e: Throwable => throw new RuntimeException("Unable to find sbt-launch.jar.", e) - } -} diff --git a/launch/src/main/scala/xsbt/boot/Update.scala b/launch/src/main/scala/xsbt/boot/Update.scala deleted file mode 100644 index c856562b0..000000000 --- a/launch/src/main/scala/xsbt/boot/Update.scala +++ /dev/null @@ -1,426 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009, 2010, 2011 Mark Harrah - */ -package xsbt.boot - -import Pre._ -import java.io.{ File, FileWriter, PrintWriter, Writer } -import java.util.concurrent.Callable -import java.util.regex.Pattern -import java.util.Properties - -import org.apache.ivy.{ core, plugins, util, Ivy } -import core.LogOptions -import core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, DefaultResolutionCacheManager } -import core.event.EventManager -import core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } -import core.module.descriptor.{ Configuration => IvyConfiguration, DefaultDependencyArtifactDescriptor, DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor } -import core.module.descriptor.{ Artifact => IArtifact, DefaultExcludeRule, DependencyDescriptor, ExcludeRule } -import core.report.ResolveReport -import core.resolve.{ ResolveEngine, ResolveOptions } -import core.retrieve.{ RetrieveEngine, RetrieveOptions } -import core.sort.SortEngine -import core.settings.IvySettings -import plugins.matcher.{ ExactPatternMatcher, PatternMatcher } -import plugins.resolver.{ BasicResolver, ChainResolver, FileSystemResolver, IBiblioResolver, URLResolver } -import util.{ DefaultMessageLogger, filter, Message, MessageLoggerEngine, url } -import filter.Filter -import url.CredentialsStore - -import BootConfiguration._ - -sealed trait UpdateTarget { 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, val classifiers: List[String], val tpe: String) extends UpdateTarget - -final class UpdateConfiguration(val bootDirectory: File, val ivyHome: Option[File], val scalaOrg: String, - val scalaVersion: Option[String], val repositories: List[xsbti.Repository], val checksums: List[String]) { - val resolutionCacheBase = new File(bootDirectory, "resolution-cache") - def getScalaVersion = scalaVersion match { case Some(sv) => sv; case None => "" } -} - -final class UpdateResult(val success: Boolean, val scalaVersion: Option[String], val appVersion: Option[String]) { - @deprecated("Please use the other constructor providing appVersion.", "0.13.2") - def this(success: Boolean, scalaVersion: Option[String]) = this(success, scalaVersion, None) -} - -/** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/ -final class Update(config: UpdateConfiguration) { - import config.{ bootDirectory, checksums, getScalaVersion, ivyHome, repositories, resolutionCacheBase, scalaVersion, scalaOrg } - bootDirectory.mkdirs - - private def logFile = new File(bootDirectory, UpdateLogName) - private val logWriter = new PrintWriter(new FileWriter(logFile)) - - private def addCredentials() { - val optionProps = - Option(System.getProperty("sbt.boot.credentials")) orElse - Option(System.getenv("SBT_CREDENTIALS")) map (path => - Pre.readProperties(new File(path)) - ) - optionProps match { - case Some(props) => extractCredentials("realm", "host", "user", "password")(props) - case None => () - } - extractCredentials("sbt.boot.realm", "sbt.boot.host", "sbt.boot.user", "sbt.boot.password")(System.getProperties) - } - private def extractCredentials(keys: (String, String, String, String))(props: Properties) { - val List(realm, host, user, password) = keys.productIterator.map(key => props.getProperty(key.toString)).toList - if (realm != null && host != null && user != null && password != null) - CredentialsStore.INSTANCE.addCredentials(realm, host, user, password) - } - private lazy val settings = - { - addCredentials() - val settings = new IvySettings - ivyHome match { case Some(dir) => settings.setDefaultIvyUserDir(dir); case None => } - addResolvers(settings) - settings.setVariable("ivy.checksums", checksums mkString ",") - settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName)) - settings.setBaseDir(bootDirectory) - setScalaVariable(settings, scalaVersion) - settings - } - private[this] def setScalaVariable(settings: IvySettings, scalaVersion: Option[String]): Unit = - scalaVersion match { case Some(sv) => settings.setVariable("scala", sv); case None => } - private lazy val ivy = - { - val ivy = new Ivy() { private val loggerEngine = new SbtMessageLoggerEngine; override def getLoggerEngine = loggerEngine } - ivy.setSettings(settings) - ivy.bind() - ivy - } - // should be the same file as is used in the Ivy module - private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock") - - /** The main entry point of this class for use by the Update module. It runs Ivy */ - def apply(target: UpdateTarget, reason: String): UpdateResult = - { - Message.setDefaultLogger(new SbtIvyLogger(logWriter)) - val action = new Callable[UpdateResult] { def call = lockedApply(target, reason) } - Locks(ivyLockFile, action) - } - private def lockedApply(target: UpdateTarget, reason: String): UpdateResult = - { - ivy.pushContext() - try { update(target, reason) } - catch { - case e: Exception => - e.printStackTrace(logWriter) - log(e.toString) - System.out.println(" (see " + logFile + " for complete log)") - new UpdateResult(false, None, None) - } finally { - logWriter.close() - ivy.popContext() - delete(resolutionCacheBase) - } - } - /** Runs update for the specified target (updates either the scala or appliciation jars for building the project) */ - private def update(target: UpdateTarget, reason: String): UpdateResult = - { - import IvyConfiguration.Visibility.PUBLIC - // the actual module id here is not that important - val moduleID = new DefaultModuleDescriptor(createID(SbtOrg, "boot-" + target.tpe, "1.0"), "release", null, false) - moduleID.setLastModified(System.currentTimeMillis) - moduleID.addConfiguration(new IvyConfiguration(DefaultIvyConfiguration, PUBLIC, "", new Array(0), true, null)) - // add dependencies based on which target needs updating - val dep = target match { - case u: UpdateScala => - val scalaVersion = getScalaVersion - addDependency(moduleID, scalaOrg, CompilerModuleName, scalaVersion, "default;optional(default)", u.classifiers) - val ddesc = addDependency(moduleID, scalaOrg, LibraryModuleName, scalaVersion, "default", u.classifiers) - excludeJUnit(moduleID) - val scalaOrgString = if (scalaOrg != ScalaOrg) " " + scalaOrg else "" - System.out.println("Getting" + scalaOrgString + " Scala " + scalaVersion + " " + reason + "...") - ddesc.getDependencyId - case u: UpdateApp => - val app = u.id - val resolvedName = (app.crossVersioned, scalaVersion) match { - case (xsbti.CrossValue.Full, Some(sv)) => app.name + "_" + sv - case (xsbti.CrossValue.Binary, Some(sv)) => app.name + "_" + CrossVersionUtil.binaryScalaVersion(sv) - case _ => app.name - } - val ddesc = addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", u.classifiers) - System.out.println("Getting " + app.groupID + " " + resolvedName + " " + app.getVersion + " " + reason + "...") - ddesc.getDependencyId - } - update(moduleID, target, dep) - } - /** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */ - private def update(moduleID: DefaultModuleDescriptor, target: UpdateTarget, dep: ModuleId): UpdateResult = - { - val eventManager = new EventManager - val (autoScalaVersion, depVersion) = resolve(eventManager, moduleID, dep) - // Fix up target.id with the depVersion that we know for sure is resolved (not dynamic) -- this way, `retrieve` - // will put them in the right version directory. - val target1 = (depVersion, target) match { - case (Some(dv), u: UpdateApp) => - import u._; new UpdateApp(id.copy(version = new Explicit(dv)), classifiers, tpe) - case _ => target - } - setScalaVariable(settings, autoScalaVersion) - retrieve(eventManager, moduleID, target1, autoScalaVersion) - new UpdateResult(true, autoScalaVersion, depVersion) - } - private def createID(organization: String, name: String, revision: String) = - ModuleRevisionId.newInstance(organization, name, revision) - /** Adds the given dependency to the default configuration of 'moduleID'. */ - private def addDependency(moduleID: DefaultModuleDescriptor, organization: String, name: String, revision: String, conf: String, classifiers: List[String]) = - { - val dep = new DefaultDependencyDescriptor(moduleID, createID(organization, name, revision), false, false, true) - for (c <- conf.split(";")) - dep.addDependencyConfiguration(DefaultIvyConfiguration, c) - for (classifier <- classifiers) - addClassifier(dep, name, classifier) - moduleID.addDependency(dep) - dep - } - private def addClassifier(dep: DefaultDependencyDescriptor, name: String, classifier: String) { - val extraMap = new java.util.HashMap[String, String] - if (!isEmpty(classifier)) - extraMap.put("e:classifier", classifier) - val ivyArtifact = new DefaultDependencyArtifactDescriptor(dep, name, artifactType(classifier), "jar", null, extraMap) - for (conf <- dep.getModuleConfigurations) - dep.addDependencyArtifact(conf, ivyArtifact) - } - private def excludeJUnit(module: DefaultModuleDescriptor): Unit = module.addExcludeRule(excludeRule(JUnitName, JUnitName)) - private def excludeRule(organization: String, name: String): ExcludeRule = - { - val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", "*", "*") - val rule = new DefaultExcludeRule(artifact, ExactPatternMatcher.INSTANCE, java.util.Collections.emptyMap[AnyRef, AnyRef]) - rule.addConfiguration(DefaultIvyConfiguration) - rule - } - val scalaLibraryId = ModuleId.newInstance(ScalaOrg, LibraryModuleName) - // Returns the version of the scala library, as well as `dep` (a dependency of `module`) after it's been resolved - private def resolve(eventManager: EventManager, module: ModuleDescriptor, dep: ModuleId): (Option[String], Option[String]) = - { - val resolveOptions = new ResolveOptions - // this reduces the substantial logging done by Ivy, including the progress dots when downloading artifacts - resolveOptions.setLog(LogOptions.LOG_DOWNLOAD_ONLY) - resolveOptions.setCheckIfChanged(false) - val resolveEngine = new ResolveEngine(settings, eventManager, new SortEngine(settings)) - val resolveReport = resolveEngine.resolve(module, resolveOptions) - if (resolveReport.hasError) { - logExceptions(resolveReport) - val seen = new java.util.LinkedHashSet[Any] - seen.addAll(resolveReport.getAllProblemMessages) - System.out.println(seen.toArray.mkString(System.getProperty("line.separator"))) - error("Error retrieving required libraries") - } - val modules = moduleRevisionIDs(resolveReport) - extractVersion(modules, scalaLibraryId) -> extractVersion(modules, dep) - } - private[this] def extractVersion(modules: Seq[ModuleRevisionId], dep: ModuleId): Option[String] = - { - modules collectFirst { case m if m.getModuleId.equals(dep) => m.getRevision } - } - private[this] def moduleRevisionIDs(report: ResolveReport): Seq[ModuleRevisionId] = - { - import collection.JavaConverters._ - import org.apache.ivy.core.resolve.IvyNode - report.getDependencies.asInstanceOf[java.util.List[IvyNode]].asScala map (_.getResolvedId) - } - - /** Exceptions are logged to the update log file. */ - private def logExceptions(report: ResolveReport) { - for (unresolved <- report.getUnresolvedDependencies) { - val problem = unresolved.getProblem - if (problem != null) - problem.printStackTrace(logWriter) - } - } - private final class ArtifactFilter(f: IArtifact => Boolean) extends Filter { - def accept(o: Any) = o match { case a: IArtifact => f(a); case _ => false } - } - /** Retrieves resolved dependencies using the given target to determine the location to retrieve to. */ - private def retrieve(eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget, autoScalaVersion: Option[String]) { - val retrieveOptions = new RetrieveOptions - val retrieveEngine = new RetrieveEngine(settings, eventManager) - val (pattern, extraFilter) = - target match { - case _: UpdateScala => (scalaRetrievePattern, const(true)) - case u: UpdateApp => (appRetrievePattern(u.id.toID), notCoreScala _) - } - val filter = (a: IArtifact) => retrieveType(a.getType) && a.getExtraAttribute("classifier") == null && extraFilter(a) - retrieveOptions.setArtifactFilter(new ArtifactFilter(filter)) - val scalaV = strictOr(scalaVersion, autoScalaVersion) - retrieveOptions.setDestArtifactPattern(baseDirectoryName(scalaOrg, scalaV) + "/" + pattern) - retrieveEngine.retrieve(module.getModuleRevisionId, retrieveOptions) - } - private[this] def notCoreScala(a: IArtifact) = a.getName match { - case LibraryModuleName | CompilerModuleName => false - case _ => true - } - private def retrieveType(tpe: String): Boolean = tpe == "jar" || tpe == "bundle" - /** Add the Sonatype OSS repositories */ - private def addResolvers(settings: IvySettings) { - val newDefault = new ChainResolver { - override def locate(artifact: IArtifact) = - if (hasImplicitClassifier(artifact)) null else super.locate(artifact) - } - newDefault.setName("redefined-public") - if (repositories.isEmpty) error("No repositories defined.") - for (repo <- repositories if includeRepo(repo)) - newDefault.add(toIvyRepository(settings, repo)) - configureCache(settings) - settings.addResolver(newDefault) - settings.setDefaultResolver(newDefault.getName) - } - // infrastructure is needed to avoid duplication between this class and the ivy/ subproject - private def hasImplicitClassifier(artifact: IArtifact): Boolean = - { - import collection.JavaConversions._ - artifact.getQualifiedExtraAttributes.keys.exists(_.asInstanceOf[String] startsWith "m:") - } - // exclude the local Maven repository for Scala -SNAPSHOTs - private def includeRepo(repo: xsbti.Repository) = !(Repository.isMavenLocal(repo) && isSnapshot(getScalaVersion)) - private def isSnapshot(scalaVersion: String) = scalaVersion.endsWith(Snapshot) - private[this] val Snapshot = "-SNAPSHOT" - private[this] val ChangingPattern = ".*" + Snapshot - private[this] val ChangingMatcher = PatternMatcher.REGEXP - private[this] def configureCache(settings: IvySettings) { - configureResolutionCache(settings) - configureRepositoryCache(settings) - } - private[this] def configureResolutionCache(settings: IvySettings) { - resolutionCacheBase.mkdirs() - val drcm = new DefaultResolutionCacheManager(resolutionCacheBase) - drcm.setSettings(settings) - settings.setResolutionCacheManager(drcm) - } - private[this] def configureRepositoryCache(settings: IvySettings) { - val cacheDir = settings.getDefaultRepositoryCacheBasedir() - val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) { - // ignore resolvers wherever possible- not ideal, but avoids issues like #704 - override def saveResolvers(descriptor: ModuleDescriptor, metadataResolverName: String, artifactResolverName: String) {} - override def findModuleInCache(dd: DependencyDescriptor, revId: ModuleRevisionId, options: CacheMetadataOptions, r: String) = { - super.findModuleInCache(dd, revId, options, null) - } - } - manager.setUseOrigin(true) - manager.setChangingMatcher(ChangingMatcher) - manager.setChangingPattern(ChangingPattern) - settings.addRepositoryCacheManager(manager) - settings.setDefaultRepositoryCacheManager(manager) - } - private def toIvyRepository(settings: IvySettings, repo: xsbti.Repository) = - { - import xsbti.Predefined._ - repo match { - case m: xsbti.MavenRepository => mavenResolver(m.id, m.url.toString) - case i: xsbti.IvyRepository => urlResolver(i.id, i.url.toString, i.ivyPattern, i.artifactPattern, i.mavenCompatible, i.descriptorOptional, i.skipConsistencyCheck) - case p: xsbti.PredefinedRepository => p.id match { - case Local => localResolver(settings.getDefaultIvyUserDir.getAbsolutePath) - case MavenLocal => mavenLocal - case MavenCentral => mavenMainResolver - case ScalaToolsReleases | SonatypeOSSReleases => mavenResolver("Sonatype Releases Repository", "https://oss.sonatype.org/content/repositories/releases") - case ScalaToolsSnapshots | SonatypeOSSSnapshots => scalaSnapshots(getScalaVersion) - } - } - } - private def onDefaultRepositoryCacheManager(settings: IvySettings)(f: DefaultRepositoryCacheManager => Unit) { - settings.getDefaultRepositoryCacheManager match { - case manager: DefaultRepositoryCacheManager => f(manager) - case _ => () - } - } - /** Uses the pattern defined in BuildConfiguration to download sbt from Google code.*/ - private def urlResolver(id: String, base: String, ivyPattern: String, artifactPattern: String, mavenCompatible: Boolean, descriptorOptional: Boolean, skipConsistencyCheck: Boolean) = - { - val resolver = new URLResolver - resolver.setName(id) - resolver.addIvyPattern(adjustPattern(base, ivyPattern)) - resolver.addArtifactPattern(adjustPattern(base, artifactPattern)) - resolver.setM2compatible(mavenCompatible) - resolver.setDescriptor(if (descriptorOptional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED) - resolver.setCheckconsistency(!skipConsistencyCheck) - resolver - } - private def adjustPattern(base: String, pattern: String): String = - (if (base.endsWith("/") || isEmpty(base)) base else (base + "/")) + pattern - private def mavenLocal = mavenResolver("Maven2 Local", "file://" + System.getProperty("user.home") + "/.m2/repository/") - /** Creates a maven-style resolver.*/ - private def mavenResolver(name: String, root: String) = - { - val resolver = new IBiblioResolver - resolver.setName(name) - resolver.setM2compatible(true) - resolver.setRoot(root) - resolver - } - private def useSecureResolvers = sys.props.get("sbt.repository.secure") map { _.toLowerCase == "true" } getOrElse true - private def centralRepositoryRoot(secure: Boolean) = (if (secure) "https" else "http") + "://repo1.maven.org/maven2/" - - /** Creates a resolver for Maven Central.*/ - private def mavenMainResolver = defaultMavenResolver("Maven Central") - /** Creates a maven-style resolver with the default root.*/ - private def defaultMavenResolver(name: String) = - mavenResolver(name, centralRepositoryRoot(useSecureResolvers)) - private def localResolver(ivyUserDirectory: String) = - { - val localIvyRoot = ivyUserDirectory + "/local" - val resolver = new FileSystemResolver - resolver.setName(LocalIvyName) - resolver.addIvyPattern(localIvyRoot + "/" + LocalIvyPattern) - resolver.addArtifactPattern(localIvyRoot + "/" + LocalArtifactPattern) - resolver - } - private val SnapshotPattern = Pattern.compile("""(\d+).(\d+).(\d+)-(\d{8})\.(\d{6})-(\d+|\+)""") - private def scalaSnapshots(scalaVersion: String) = - { - val m = SnapshotPattern.matcher(scalaVersion) - if (m.matches) { - val base = List(1, 2, 3).map(m.group).mkString(".") - val pattern = "https://oss.sonatype.org/content/repositories/snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision](-[classifier]).[ext]" - - val resolver = new URLResolver - resolver.setName("Sonatype OSS Snapshots") - resolver.setM2compatible(true) - resolver.addArtifactPattern(pattern) - resolver - } else - mavenResolver("Sonatype Snapshots Repository", "https://oss.sonatype.org/content/repositories/snapshots") - } - - /** Logs the given message to a file and to the console. */ - private def log(msg: String) = - { - try { logWriter.println(msg) } - catch { case e: Exception => System.err.println("Error writing to update log file: " + e.toString) } - System.out.println(msg) - } -} - -import SbtIvyLogger.{ acceptError, acceptMessage, isAlwaysIgnoreMessage } - -/** - * A custom logger for Ivy to ignore the messages about not finding classes - * intentionally filtered using proguard and about 'unknown resolver'. - */ -private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageLogger(Message.MSG_INFO) { - override def log(msg: String, level: Int): Unit = - if (isAlwaysIgnoreMessage(msg)) () - else { - logWriter.println(msg) - if (level <= getLevel && acceptMessage(msg)) - System.out.println(msg) - } - override def rawlog(msg: String, level: Int) { log(msg, level) } - /** This is a hack to filter error messages about 'unknown resolver ...'. */ - override def error(msg: String) = if (acceptError(msg)) super.error(msg) -} -private final class SbtMessageLoggerEngine extends MessageLoggerEngine { - /** This is a hack to filter error messages about 'unknown resolver ...'. */ - override def error(msg: String) = if (acceptError(msg)) super.error(msg) -} -private object SbtIvyLogger { - val IgnorePrefix = "impossible to define" - val UnknownResolver = "unknown resolver" - def acceptError(msg: String) = acceptMessage(msg) && !msg.startsWith(UnknownResolver) - def acceptMessage(msg: String) = (msg ne null) && !msg.startsWith(IgnorePrefix) - def isAlwaysIgnoreMessage(msg: String): Boolean = - (msg eq null) || - (msg startsWith "setting 'http.proxyPassword'") -} diff --git a/launch/src/main/scala/xsbt/boot/Using.scala b/launch/src/main/scala/xsbt/boot/Using.scala deleted file mode 100644 index cf03a3067..000000000 --- a/launch/src/main/scala/xsbt/boot/Using.scala +++ /dev/null @@ -1,40 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2009 Mark Harrah - */ -package xsbt.boot - -import java.io.{ Closeable, File, FileInputStream, FileOutputStream, InputStream, OutputStream } - -object Using { - def apply[R <: Closeable, T](create: R)(f: R => T): T = withResource(create)(f) - def withResource[R <: Closeable, T](r: R)(f: R => T): T = try { f(r) } finally { r.close() } -} - -object Copy { - def apply(files: List[File], toDirectory: File): Boolean = files.map(file => apply(file, toDirectory)).contains(true) - def apply(file: File, toDirectory: File): Boolean = - { - toDirectory.mkdirs() - val to = new File(toDirectory, file.getName) - val missing = !to.exists - if (missing) { - Using(new FileInputStream(file)) { in => - Using(new FileOutputStream(to)) { out => - transfer(in, out) - } - } - } - missing - } - def transfer(in: InputStream, out: OutputStream) { - val buffer = new Array[Byte](8192) - def next() { - val read = in.read(buffer) - if (read > 0) { - out.write(buffer, 0, read) - next() - } - } - next() - } -} \ No newline at end of file diff --git a/launch/test-sample/src/main/scala/xsbt/boot/test/Apps.scala b/launch/test-sample/src/main/scala/xsbt/boot/test/Apps.scala deleted file mode 100644 index ba377d61b..000000000 --- a/launch/test-sample/src/main/scala/xsbt/boot/test/Apps.scala +++ /dev/null @@ -1,42 +0,0 @@ -/** These are packaged and published locally and the resulting artifact is used to test the launcher.*/ -package xsbt.boot.test - -class Exit(val code: Int) extends xsbti.Exit -final class MainException(message: String) extends RuntimeException(message) -final class ArgumentTest extends xsbti.AppMain { - def run(configuration: xsbti.AppConfiguration) = - if (configuration.arguments.length == 0) - throw new MainException("Arguments were empty") - else - new Exit(0) -} -class AppVersionTest extends xsbti.AppMain { - def run(configuration: xsbti.AppConfiguration) = - { - val expected = configuration.arguments.headOption.getOrElse("") - if (configuration.provider.id.version == expected) - new Exit(0) - else - throw new MainException("app version was " + configuration.provider.id.version + ", expected: " + expected) - } -} -class ExtraTest extends xsbti.AppMain { - def run(configuration: xsbti.AppConfiguration) = - { - configuration.arguments.foreach { arg => - if (getClass.getClassLoader.getResource(arg) eq null) - throw new MainException("Could not find '" + arg + "'") - } - new Exit(0) - } -} -object PlainArgumentTestWithReturn { - def main(args: Array[String]): Int = - if (args.length == 0) 1 - else 0 -} -object PlainArgumentTest { - def main(args: Array[String]): Unit = - if (args.length == 0) throw new MainException("Arguments were empty") - else () -} \ No newline at end of file diff --git a/launch/test-sample/src/main/scala/xsbt/boot/test/Servers.scala b/launch/test-sample/src/main/scala/xsbt/boot/test/Servers.scala deleted file mode 100644 index bd1fff114..000000000 --- a/launch/test-sample/src/main/scala/xsbt/boot/test/Servers.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** These are packaged and published locally and the resulting artifact is used to test the launcher.*/ -package xsbt.boot.test - -import java.net.Socket -import java.net.SocketTimeoutException - -class EchoServer extends xsbti.ServerMain { - def start(configuration: xsbti.AppConfiguration): xsbti.Server = - { - object server extends xsbti.Server { - // TODO - Start a server. - val serverSocket = new java.net.ServerSocket(0) - val port = serverSocket.getLocalPort - val addr = serverSocket.getInetAddress.getHostAddress - override val uri = new java.net.URI(s"http://${addr}:${port}") - // Check for stop every second. - serverSocket.setSoTimeout(1000) - object serverThread extends Thread { - private val running = new java.util.concurrent.atomic.AtomicBoolean(true) - override def run(): Unit = { - while (running.get) try { - val clientSocket = serverSocket.accept() - // Handle client connections - object clientSocketThread extends Thread { - override def run(): Unit = { - echoTo(clientSocket) - } - } - clientSocketThread.start() - } catch { - case e: SocketTimeoutException => // Ignore - } - } - // Simple mechanism to dump input to output. - private def echoTo(socket: Socket): Unit = { - val input = new java.io.BufferedReader(new java.io.InputStreamReader(socket.getInputStream)) - val output = new java.io.BufferedWriter(new java.io.OutputStreamWriter(socket.getOutputStream)) - import scala.util.control.Breaks._ - try { - // Lame way to break out. - breakable { - def read(): Unit = input.readLine match { - case null => () - case "kill" => - running.set(false) - serverSocket.close() - break() - case line => - output.write(line) - output.flush() - read() - } - read() - } - } finally { - output.close() - input.close() - socket.close() - } - } - } - // Start the thread immediately - serverThread.start() - override def awaitTermination(): xsbti.MainResult = { - serverThread.join() - new Exit(0) - } - } - server - } - -} \ No newline at end of file diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 57e2c9f88..cdfc07ed2 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -19,6 +19,7 @@ object Dependencies { lazy val specs2 = "org.specs2" %% "specs2" % "2.3.11" lazy val junit = "junit" % "junit" % "4.11" lazy val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0-SNAPSHOT" + lazy val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.0-SNAPSHOT" private def scala211Module(name: String, moduleVersion: String) = Def.setting { diff --git a/project/SbtLauncherPlugin.scala b/project/SbtLauncherPlugin.scala new file mode 100644 index 000000000..d84ba0e9f --- /dev/null +++ b/project/SbtLauncherPlugin.scala @@ -0,0 +1,50 @@ +import sbt._ +import Keys._ + + +object SbtLauncherPlugin extends AutoPlugin { + override def requires = plugins.IvyPlugin + + object autoImport { + val SbtLaunchConfiguration = config("sbt-launch") + val sbtLaunchJar = taskKey[File]("constructs an sbt-launch.jar for this version of sbt.") + val rawSbtLaunchJar = taskKey[File]("The released version of the sbt-launcher we use to bundle this application.") + } + import autoImport._ + + override def projectConfigurations: Seq[Configuration] = Seq(SbtLaunchConfiguration) + override def projectSettings: Seq[Setting[_]] = Seq( + libraryDependencies += Dependencies.rawLauncher % SbtLaunchConfiguration.name, + rawSbtLaunchJar := { + Classpaths.managedJars(SbtLaunchConfiguration, Set("jar"), update.value).headOption match { + case Some(jar) => jar.data + case None => sys.error(s"Could not resolve sbt launcher!, dependencies := ${libraryDependencies.value}") + } + }, + sbtLaunchJar := { + val propFiles = (resources in Compile).value + val propFileLocations = + for(file <- propFiles; if file.getName != "resources") yield { + if(file.getName == "sbt.boot.properties") "sbt/sbt.boot.properties" -> file + else file.getName -> file + } + propFileLocations foreach println + // TODO - We need to inject the appropriate boot.properties file for this version of sbt. + rebundle(rawSbtLaunchJar.value, propFileLocations.toMap, target.value / "sbt-launch.jar") + } + ) + + + + def rebundle(jar: File, overrides: Map[String, File], target: File): File = { + // TODO - Check if we should rebuild the jar or not.... + IO.withTemporaryDirectory { dir => + IO.unzip(jar, dir) + IO.copy(overrides.map({ case (n, f) => (f, dir / n)}), overwrite = true) + // TODO - is the ok for creating a jar? + IO.zip((dir.*** --- dir) x relativeTo(dir), target) + } + target + } + +} \ No newline at end of file