 	public let verb = "build"
 	public let function = "Build the project's dependencies"
 
-	public func run(mode: CommandMode) -> Result<(), CommandantError> {
-		return ColdSignal.fromResult(BuildOptions.evaluate(mode))
-			.map { self.buildWithOptions($0) }
-			.merge(identity)
-			.wait()
+	public func run(mode: CommandMode) -> Result<(), CommandantError<CarthageError>> {
+		return producerWithOptions(BuildOptions.evaluate(mode))
+			|> joinMap(.Merge) { options in
+				return self.buildWithOptions(options)
+					|> promoteErrors
+			}
+			|> waitOnCommand
 	}
 
 	/// Builds a project with the given options.
-	public func buildWithOptions(options: BuildOptions) -> ColdSignal<()> {
+	public func buildWithOptions(options: BuildOptions) -> SignalProducer<(), CarthageError> {
 		return self.createLoggingSink(options)
-			.map { (stdoutSink, temporaryURL) -> ColdSignal<()> in
+			|> joinMap(.Merge) { (stdoutSink, temporaryURL) -> SignalProducer<(), CarthageError> in
 				let directoryURL = NSURL.fileURLWithPath(options.directoryPath, isDirectory: true)!
 
 				let (stdoutSignal, schemeSignals) = self.buildProjectInDirectoryURL(directoryURL, options: options)

		return self(configuration: configuration, buildPlatform: buildPlatform, skipCurrent: skipCurrent, colorOptions: colorOptions, verbose: verbose, directoryPath: directoryPath)
	}

	public static func evaluate(m: CommandMode) -> Result<BuildOptions, CommandantError> {
		return create
			<*> m <| Option(key: "configuration", defaultValue: "Release", usage: "the Xcode configuration to build")
			<*> m <| Option(key: "platform", defaultValue: .All, usage: "the platform to build for (one of ‘all’, ‘Mac’, or ‘iOS’)")

				let formatting = options.colorOptions.formatting

				return schemeSignals
					|> join(.Concat)
					|> on(started: {
						if let temporaryURL = temporaryURL {
							carthage.println(formatting.bullets + "xcodebuild output can be found in " + formatting.path(string: temporaryURL.path!))
						}
					}, next: { (project, scheme) in
						carthage.println(formatting.bullets + "Building scheme " + formatting.quote(scheme) + " in " + formatting.projectName(string: project.description))
					})
					|> then(.empty)
			}
	}

	/// Builds the project in the given directory, using the given options.
	///
	/// Returns a hot signal of `stdout` from `xcodebuild`, and a cold signal of
	/// cold signals representing each scheme being built.
	private func buildProjectInDirectoryURL(directoryURL: NSURL, options: BuildOptions) -> (Signal<NSData, NoError>, SignalProducer<BuildSchemeProducer, CarthageError>) {
		let (stdoutSignal, stdoutSink) = Signal<NSData, NoError>.pipe()
		let project = Project(directoryURL: directoryURL)

		var buildProducer = project.loadCombinedCartfile()
			|> map { _ in project }
			|> catch { error in
				if options.skipCurrent {
					return SignalProducer(error: error)
				} else {
					// Ignore Cartfile loading failures. Assume the user just
					// wants to build the enclosing project.
					return .empty
				}
			}
			|> joinMap(.Merge) { project in
				return project.migrateIfNecessary(options.colorOptions)
					|> on(next: carthage.println)
					|> then(SignalProducer(value: project))
			}
			|> joinMap(.Merge) { (project: Project) -> SignalProducer<BuildSchemeProducer, CarthageError> in
				let (dependenciesOutput, dependencies) = project.buildCheckedOutDependenciesWithConfiguration(options.configuration, forPlatform: options.buildPlatform.platform)
				dependenciesOutput.observe(stdoutSink)

				return dependencies
			}

		if !options.skipCurrent {
			let (currentOutput, currentProducers) = buildInDirectory(directoryURL, withConfiguration: options.configuration, platform: options.buildPlatform.platform)
			currentOutput.observe(stdoutSink)

			buildProducer = buildProducer |> concat(currentProducers)
		}

		return (stdoutSignal, buildProducer)
	}

	/// Creates a sink for logging, returning the sink and the URL to any
	/// temporary file on disk.
	private func createLoggingSink(options: BuildOptions) -> SignalProducer<(FileSink<NSData>, NSURL?), CarthageError> {
		if options.verbose {
			let out: (FileSink<NSData>, NSURL?) = (FileSink.standardOutputSink(), nil)
			return SignalProducer(value: out)
		} else {
			return FileSink.openTemporaryFile()
				|> map { sink, URL in (sink, .Some(URL)) }
				|> mapError { error in
					let temporaryDirectoryURL = NSURL.fileURLWithPath(NSTemporaryDirectory(), isDirectory: true)!
					return .WriteFailed(temporaryDirectoryURL, error)
				}
		}
	}
}
