diff --git a/launch/interface/src/main/java/xsbti/AppProvider.java b/launch/interface/src/main/java/xsbti/AppProvider.java index 36b692b83..24744c83c 100644 --- a/launch/interface/src/main/java/xsbti/AppProvider.java +++ b/launch/interface/src/main/java/xsbti/AppProvider.java @@ -2,6 +2,11 @@ package xsbti; import java.io.File; +/** + * This represents an interface that can generate applications. + * + * An application is somethign which will run and return an exit value. + */ public interface AppProvider { /** Returns the ScalaProvider that this AppProvider will use. */ @@ -34,5 +39,8 @@ public interface AppProvider /** 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/ComponentProvider.java b/launch/interface/src/main/java/xsbti/ComponentProvider.java index 5eb234a22..424e31989 100644 --- a/launch/interface/src/main/java/xsbti/ComponentProvider.java +++ b/launch/interface/src/main/java/xsbti/ComponentProvider.java @@ -2,12 +2,52 @@ 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); - // null if locking disabled + /** + * @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/src/main/scala/xsbt/boot/Launch.scala b/launch/src/main/scala/xsbt/boot/Launch.scala index 169523efe..4604ce2ef 100644 --- a/launch/src/main/scala/xsbt/boot/Launch.scala +++ b/launch/src/main/scala/xsbt/boot/Launch.scala @@ -15,42 +15,50 @@ object Launch { def apply(arguments: List[String]): Option[Int] = apply( (new File("")).getAbsoluteFile , arguments ) - def apply(currentDirectory: File, arguments: List[String]): Option[Int] = - Configuration.find(arguments, currentDirectory) match { case (configLocation, newArguments) => configured(currentDirectory, configLocation, newArguments) } - - def configured(currentDirectory: File, configLocation: URL, arguments: List[String]): Option[Int] = - { - val config = Configuration.parse(configLocation, currentDirectory) - Find(config, currentDirectory) match { case (resolved, baseDirectory) => parsed(baseDirectory, resolved, arguments) } + def apply(currentDirectory: File, arguments: List[String]): Option[Int] = { + val (configLocation, newArguments) = Configuration.find(arguments, currentDirectory) + val config = parseAndInitializeConfig(configLocation, currentDirectory) + launch(run(Launcher(config)))(makeRunConfig(currentDirectory, config, newArguments)) } - def parsed(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Option[Int] = + /** 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) - initialized(currentDirectory, parsed, arguments) - } - def initialized(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Option[Int] = - { + parsed.logging.debug("Parsed configuration: " + parsed) val resolved = ResolveValues(parsed) resolved.logging.debug("Resolved configuration: " + resolved) - explicit(currentDirectory, resolved, arguments) + resolved } - def explicit(currentDirectory: File, explicit: LaunchConfiguration, arguments: List[String]): Option[Int] = - launch( run(Launcher(explicit)) ) ( - new RunConfiguration(explicit.getScalaVersion, explicit.app.toID, currentDirectory, arguments) ) + /** 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()