[gradle-1.12] 02/211: upstream import 0.9~rc2

Kai-Chung Yan seamlik-guest at moszumanska.debian.org
Wed Jul 1 14:17:46 UTC 2015


This is an automated email from the git hooks/post-receive script.

seamlik-guest pushed a commit to branch master
in repository gradle-1.12.

commit aea7f2775fddafa2d221092c7f023e730623913e
Author: Miguel Landaeta <miguel at miguel.cc>
Date:   Tue Nov 9 21:22:14 2010 -0430

    upstream import 0.9~rc2
---
 build.gradle                                       |  250 ++--
 buildSrc/build.gradle                              |    2 -
 .../src/main/groovy/org/gradle/build/Git.groovy    |   40 +
 .../main/groovy/org/gradle/build/Install.groovy    |   38 +
 .../main/groovy/org/gradle/build/Version.groovy    |   64 +
 .../build/docs/AssembleSampleDocsTask.groovy       |    7 +-
 .../gradle/build/docs/BuildableDOMCategory.groovy  |   59 +
 .../groovy/org/gradle/build/docs/DomBuilder.groovy |   28 +-
 .../build/docs/UserGuideTransformTask.groovy       |   50 +-
 .../build/docs/XIncludeAwareXmlProvider.groovy     |   51 +
 .../build/docs/dsl/AssembleDslDocTask.groovy       |  119 ++
 .../org/gradle/build/docs/dsl/ClassDoc.groovy      |  111 ++
 .../org/gradle/build/docs/dsl/ClassMetaData.java   |   62 +
 .../org/gradle/build/docs/dsl/DslModel.groovy      |   43 +
 .../gradle/build/docs/dsl/ExtensionMetaData.groovy |   14 +
 .../build/docs/dsl/ExtractDslMetaDataTask.groovy   |   69 ++
 .../gradle/build/docs/dsl/PropertyMetaData.java    |   39 +
 .../build/samples/WrapperProjectCreator.groovy     |    4 +-
 .../build/startscripts/unixStartScriptHead.txt     |    4 +-
 .../build/startscripts/unixStartScriptTail.txt     |    3 +
 .../build/startscripts/windowsStartScriptHead.txt  |    4 +-
 config/checkstyle/checkstyle-api.xml               |   38 +
 config/checkstyle/checkstyle.xml                   |   19 +-
 config/checkstyle/suppressions.xml                 |   11 +-
 gradle.properties                                  |    4 +-
 gradle/integTest.gradle                            |   29 +
 .../api/plugins/announce/AnnouncePlugin.groovy     |    2 +
 .../org/gradle/api/plugins/announce/Announcer.java |    4 +-
 .../api/plugins/announce/AnnouncerFactory.java     |   23 -
 .../announce/DefaultAnnouncerFactory.groovy        |   48 -
 .../org/gradle/api/plugins/announce/Growl.groovy   |   28 -
 .../gradle/api/plugins/announce/NotifySend.groovy  |   48 -
 .../org/gradle/api/plugins/announce/Snarl.groovy   |   74 --
 .../org/gradle/api/plugins/announce/Twitter.groovy |   69 --
 .../announce/internal/AnnouncerFactory.java        |   25 +
 .../internal/DefaultAnnouncerFactory.groovy        |   51 +
 .../api/plugins/announce/internal/Growl.groovy     |   29 +
 .../plugins/announce/internal/NotifySend.groovy    |   49 +
 .../api/plugins/announce/internal/Snarl.groovy     |   76 ++
 .../api/plugins/announce/internal/Twitter.groovy   |   70 ++
 .../gradle/api/plugins/announce/package-info.java  |   20 +
 .../announce/AnnouncePluginConventionTest.groovy   |    1 +
 .../announce/DefaultAnnouncerFactoryTest.groovy    |   62 -
 .../api/plugins/announce/NotifySendTest.groovy     |   60 -
 .../gradle/api/plugins/announce/SnarlTest.groovy   |   41 -
 .../internal/DefaultAnnouncerFactoryTest.groovy    |   63 +
 .../announce/internal/NotifySendTest.groovy        |   58 +
 .../api/plugins/announce/internal/SnarlTest.groovy |   41 +
 .../org/gradle/api/plugins/antlr/AntlrPlugin.java  |  122 +-
 .../plugins/antlr/AntlrSourceVirtualDirectory.java |   53 +-
 .../antlr/AntlrSourceVirtualDirectoryImpl.java     |   61 -
 .../org/gradle/api/plugins/antlr/AntlrTask.java    |  169 ++-
 .../internal/AntlrSourceVirtualDirectoryImpl.java  |   62 +
 .../api/plugins/antlr/internal/GenerationPlan.java |   79 ++
 .../antlr/internal/GenerationPlanBuilder.java      |  131 ++
 .../plugins/antlr/internal/GrammarDelegate.java    |  155 +++
 .../antlr/internal/GrammarFileMetadata.java        |   61 +
 .../plugins/antlr/internal/GrammarMetadata.java    |   94 ++
 .../plugins/antlr/internal/MetadataExtracter.java  |   90 ++
 .../gradle/api/plugins/antlr/internal/XRef.java    |   97 ++
 .../plugins/antlr/metadata/GrammarDelegate.java    |  164 ---
 .../antlr/metadata/GrammarFileMetadata.java        |   61 -
 .../plugins/antlr/metadata/GrammarMetadata.java    |  100 --
 .../plugins/antlr/metadata/MetadataExtracter.java  |   98 --
 .../gradle/api/plugins/antlr/metadata/XRef.java    |   97 --
 .../org/gradle/api/plugins/antlr/package-info.java |   20 +
 .../api/plugins/antlr/plan/GenerationPlan.java     |   79 --
 .../plugins/antlr/plan/GenerationPlanBuilder.java  |  148 ---
 .../gradle-code-quality/code-quality.gradle        |   18 +-
 .../org/gradle/api/plugins/quality/Checkstyle.java |    3 +
 .../org/gradle/api/plugins/quality/CodeNarc.java   |    3 +
 .../gradle/api/plugins/quality/package-info.java   |   20 +
 subprojects/gradle-core/core.gradle                |   30 +-
 .../integtests/ArchiveIntegrationTest.groovy       |   44 +-
 .../BackwardsCompatibilityIntegrationTest.groovy   |   55 +-
 .../BuildScriptExecutionIntegrationTest.groovy     |   30 +
 .../integtests/CommandLineIntegrationTest.groovy   |    5 -
 .../gradle/integtests/IdeaIntegrationTest.groovy   |   52 +-
 .../IncrementalTestIntegrationTest.groovy          |    4 +-
 .../gradle/integtests/JUnitIntegrationTest.groovy  |   14 +-
 .../integtests/LoggingIntegrationTest.groovy       |  423 ++++---
 .../SamplesJavaMultiProjectIntegrationTest.groovy  |    2 +-
 .../integtests/UserGuideSamplesRunner.groovy       |   37 +-
 .../integtests/WorkerProcessIntegrationTest.java   |    2 +-
 .../fixtures/AbstractGradleExecuter.java           |    2 +-
 .../fixtures/BasicGradleDistribution.java          |   24 +
 .../integtests/fixtures/ExecutionFailure.java      |   12 +-
 .../integtests/fixtures/ForkingGradleExecuter.java |   18 +-
 .../integtests/fixtures/GradleDistribution.java    |   17 +-
 .../fixtures/GradleDistributionExecuter.java       |    4 +-
 .../fixtures/InProcessGradleExecuter.java          |   55 +-
 .../fixtures/PreviousGradleVersionExecuter.groovy  |   69 ++
 .../org/gradle/integtests/fixtures/Sample.java     |   42 +-
 .../gradle/integtests/fixtures/TestResources.java  |   11 +-
 .../org/gradle/integtests/fixtures/UsesSample.java |   27 +
 .../testng/SampleTestNGIntegrationTest.groovy      |   74 ++
 .../integtests/testng/TestNGExecutionResult.groovy |  151 +++
 .../testng/TestNGIntegrationProject.groovy         |    4 +-
 .../integtests/testng/TestNGIntegrationTest.groovy |  202 +--
 .../integtests/testng/TestNgExecutionResult.groovy |  145 ---
 .../canHaveCycleInProjectDependencies/build.gradle |   15 +-
 .../canCreateAndDeleteMetaData/api/build.gradle    |    4 -
 .../canCreateAndDeleteMetaData/build.gradle        |   41 -
 .../{expectedApiModule.xml => api/api.iml.xml}     |    0
 .../{expectedRootModule.xml => root.iml.xml}       |    0
 .../{expectedProjectFile.xml => root.ipr.xml}      |    0
 .../{expectedIwsFile.xml => root.iws.xml}          |    0
 .../webservice.iml.xml}                            |    0
 .../webservice/build.gradle                        |    4 -
 .../build.gradle                                   |    4 +
 .../expectedFiles/root.ipr.xml                     |  103 ++
 .../settings.gradle                                |    3 +
 .../worksWithAnEmptyProject/build.gradle           |    1 +
 .../expectedFiles/root.iml.xml}                    |    0
 .../expectedFiles/root.ipr.xml                     |  102 ++
 .../worksWithAnEmptyProject/settings.gradle        |    2 +
 .../expectedFiles/root/root.iml.xml                |   18 +
 .../expectedFiles/root/root.ipr.xml                |  104 ++
 .../expectedFiles/top-level.iml.xml                |   18 +
 .../worksWithNonStandardLayout/root/build.gradle   |    4 +
 .../worksWithNonStandardLayout/settings.gradle     |    6 +
 .../build.gradle                                   |    2 +-
 .../canHaveMultipleTestTaskInstances/build.gradle  |   21 +
 .../src/test/java/org/gradle/Test1.java            |    9 +
 .../src/test/java/org/gradle/Test2.java            |    9 +
 .../LoggingIntegrationTest}/logging/build.gradle   |    0
 .../logging/buildSrc/build.gradle                  |    0
 .../logging/external.gradle                        |    0
 .../LoggingIntegrationTest}/logging/init.gradle    |    0
 .../logging/nestedBuild/build.gradle               |    0
 .../logging/nestedBuild/buildSrc/build.gradle      |    0
 .../logging/project1/build.gradle                  |   77 ++
 .../logging/project2/build.gradle                  |    0
 .../logging/settings.gradle                        |    0
 .../multiThreaded/build.gradle                     |   26 +
 .../canListenForTestResults/build.gradle           |   20 +
 .../src/test/java/AppException.java                |    2 +
 .../src/test/java/SomeTest.java                    |   20 +
 .../executesTestsInCorrectEnvironment/build.gradle |    9 +
 .../src/test/java/org/gradle/OkTest.java           |   35 +
 .../groovyJdk15Failing/build.gradle                |   17 +
 .../src/main/groovy/org/gradle/Ok.groovy           |    0
 .../src/test/groovy/org/gradle/BadTest.groovy      |    0
 .../groovyJdk15Passing/build.gradle                |   17 +
 .../src/main/groovy/org/gradle/Ok.groovy           |    0
 .../src/test/groovy/org/gradle/OkTest.groovy       |    0
 .../javaJdk14Failing/build.gradle                  |   16 +
 .../src/main/java/org/gradle/Ok.java               |    0
 .../src/test/java/org/gradle/BadTest.java          |    0
 .../javaJdk15Failing/build.gradle                  |   15 +
 .../src/main/java/org/gradle/Ok.java               |    0
 .../src/test/java/org/gradle/BadTest.java          |    0
 .../src/test/java/org/gradle/BrokenAfterSuite.java |    0
 .../org/gradle/TestWithBrokenMethodDependency.java |   30 +
 .../test/java/org/gradle/TestWithBrokenSetup.java  |    0
 .../groovy/org/gradle/BuildExceptionReporter.java  |  182 ++-
 .../src/main/groovy/org/gradle/BuildLogger.java    |    9 +-
 .../main/groovy/org/gradle/BuildResultLogger.java  |   23 +-
 .../org/gradle/CommandLineArgumentException.java   |    2 +
 .../src/main/groovy/org/gradle/GradleLauncher.java |   19 +-
 .../groovy/org/gradle/GradleLauncherFactory.java   |    4 +-
 .../src/main/groovy/org/gradle/StartParameter.java |   83 +-
 .../groovy/org/gradle/TaskExecutionLogger.java     |    2 +-
 .../src/main/groovy/org/gradle/api/Action.java     |    4 +-
 .../main/groovy/org/gradle/api/DefaultTask.java    |    2 +-
 .../org/gradle/api/LocationAwareException.java     |    3 +-
 .../src/main/groovy/org/gradle/api/Plugin.java     |    1 +
 .../src/main/groovy/org/gradle/api/Project.java    |   80 +-
 .../org/gradle/api/ProjectEvaluationListener.java  |    2 +-
 .../src/main/groovy/org/gradle/api/Task.java       |   22 +-
 .../main/groovy/org/gradle/api/Transformer.java    |    2 +
 .../org/gradle/api/artifacts/Configuration.java    |    2 +-
 .../org/gradle/api/artifacts/IvyObjectBuilder.java |   43 -
 .../ProjectDependenciesBuildInstruction.java       |   64 -
 .../gradle/api/artifacts/ProjectDependency.java    |    2 +-
 .../org/gradle/api/artifacts/ResolvedArtifact.java |    2 +
 .../gradle/api/artifacts/ResolvedDependency.java   |    6 +-
 .../api/artifacts/dsl/DependencyHandler.java       |   13 +-
 .../api/artifacts/dsl/RepositoryHandler.java       |    2 +
 .../artifacts/dsl/RepositoryHandlerFactory.java    |   29 -
 .../api/artifacts/indexing/IndexFileUtil.java      |   32 -
 .../api/artifacts/indexing/JarArtifactIndexer.java |   82 --
 .../artifacts/indexing/JarFilePackageListener.java |   24 -
 .../artifacts/indexing/JarFilePackageLister.java   |   71 --
 .../api/artifacts/maven/Conf2ScopeMapping.java     |    6 +-
 .../artifacts/maven/GroovyPomFilterContainer.java  |   62 -
 .../org/gradle/api/artifacts/maven/MavenPom.java   |   34 +-
 .../gradle/api/artifacts/maven/MavenResolver.java  |    2 +
 .../api/artifacts/maven/PomFilterContainer.java    |   44 +-
 .../gradle/api/artifacts/maven/PublishFilter.java  |    2 +
 .../gradle/api/artifacts/maven/XmlProvider.java    |   26 +-
 .../artifacts/repositories/InternalRepository.java |   24 -
 .../api/artifacts/repositories/WebdavResolver.java |    2 +
 .../api/artifacts/specs/DependencySpecs.java       |   50 +-
 .../api/artifacts/specs/DependencyTypeSpec.java    |   62 -
 .../org/gradle/api/artifacts/specs/Type.java       |    3 +
 .../org/gradle/api/file/ContentFilterable.java     |    3 +
 .../groovy/org/gradle/api/file/CopyAction.java     |   24 -
 .../org/gradle/api/file/CopyProcessingSpec.java    |    3 +
 .../groovy/org/gradle/api/file/CopySourceSpec.java |    2 +-
 .../groovy/org/gradle/api/file/DeleteAction.java   |    2 +
 .../groovy/org/gradle/api/file/FileCollection.java |    3 +
 .../groovy/org/gradle/api/file/RelativePath.java   |    2 +-
 .../groovy/org/gradle/api/file/package-info.java   |   20 +
 .../api/initialization/dsl/package-info.java       |   20 +
 .../api/internal/AbstractClassPathProvider.java    |    6 +-
 .../org/gradle/api/internal/AbstractTask.java      |    4 +-
 .../api/internal/CachingDirectedGraphWalker.java   |    2 -
 .../gradle/api/internal/DomainObjectContext.java   |    2 +-
 .../groovy/org/gradle/api/internal/Factory.java    |   29 +
 .../org/gradle/api/internal/GradleInternal.java    |    2 +-
 .../org/gradle/api/internal/GraphAggregator.java   |    1 -
 .../org/gradle/api/internal/XmlTransformer.java    |  156 +++
 .../ProjectDependenciesBuildInstruction.java       |   64 +
 .../DefaultConfigurationContainer.java             |    2 +-
 .../configurations/DependencyMetaDataProvider.java |    2 +-
 .../dependencies/DefaultProjectDependency.java     |    2 +-
 .../artifacts/dsl/AbstractScriptTransformer.java   |    4 +
 .../artifacts/dsl/ClasspathScriptTransformer.java  |   13 +-
 .../dsl/DefaultRepositoryHandlerFactory.java       |   16 +-
 .../artifacts/dsl/FixMainScriptTransformer.java    |    3 +
 .../SharedConventionRepositoryHandlerFactory.java  |   45 +
 .../DefaultProjectDependencyFactory.java           |    2 +-
 .../ivyservice/DefaultIvyDependencyResolver.java   |    1 -
 .../ivyservice/DefaultResolverFactory.java         |   14 +-
 .../ivyservice/DefaultSettingsConverter.java       |    8 +-
 .../artifacts/ivyservice/IvyLoggingAdaper.java     |   47 +
 .../PublishModuleDescriptorConverter.java          |    2 +-
 .../artifacts/publish/maven/DefaultMavenPom.java   |   24 +-
 .../publish/maven/DefaultMavenPomFactory.java      |    5 +-
 .../artifacts/publish/maven/MavenPomFactory.java   |   25 -
 .../maven/deploy/AbstractMavenResolver.java        |   17 +
 .../publish/maven/deploy/BaseMavenDeployer.java    |    9 +-
 .../publish/maven/deploy/BaseMavenInstaller.java   |    9 +-
 .../maven/deploy/BasePomFilterContainer.java       |   35 +-
 .../maven/deploy/DefaultDeployTaskFactory.java     |    6 +-
 .../maven/deploy/DefaultInstallTaskFactory.java    |    6 +-
 .../publish/maven/deploy/DeployTaskFactory.java    |   23 -
 .../publish/maven/deploy/InstallTaskFactory.java   |   23 -
 .../groovy/DefaultGroovyMavenDeployer.groovy       |   24 +-
 .../groovy/DefaultGroovyMavenInstaller.groovy      |   48 -
 .../groovy/DefaultGroovyPomFilterContainer.groovy  |   52 -
 .../maven/pombuilder/CustomModelBuilder.java       |    2 -
 .../repositories/DefaultInternalRepository.java    |   12 +-
 .../artifacts/repositories/InternalRepository.java |   24 +
 .../DefaultTaskArtifactStateRepository.java        |    2 +-
 .../gradle/api/internal/file/RelativePathSpec.java |   32 +
 .../internal/file/archive/TarCopySpecVisitor.java  |    2 +-
 .../internal/file/archive/ZipCopySpecVisitor.java  |    2 +-
 .../api/internal/file/copy/ArchiveCopyAction.java  |    2 -
 .../gradle/api/internal/file/copy/CopyAction.java  |   25 +
 .../api/internal/file/copy/CopySpecImpl.java       |    6 +-
 .../api/internal/file/copy/CopySpecVisitor.java    |    1 -
 .../file/copy/DelegatingCopySpecVisitor.java       |    1 -
 .../internal/file/copy/EmptyCopySpecVisitor.java   |    1 -
 .../api/internal/file/copy/FileCopyAction.java     |    2 -
 .../internal/file/copy/FileCopySpecVisitor.java    |    1 -
 .../internal/file/copy/SyncCopySpecVisitor.java    |    1 -
 .../DefaultScriptHandlerFactory.java               |   11 +-
 .../api/internal/project/AbstractProject.java      |   94 +-
 .../api/internal/project/AntBuilderFactory.java    |   25 -
 .../internal/project/DefaultAntBuilderFactory.java |    6 +-
 .../internal/project/DefaultServiceRegistry.java   |  130 +-
 .../internal/project/GlobalServicesRegistry.java   |   23 +-
 .../project/GradleInternalServiceRegistry.java     |    2 +-
 .../api/internal/project/ProjectFactory.java       |    2 +-
 .../api/internal/project/ProjectInternal.java      |    8 +-
 .../project/ProjectInternalServiceRegistry.java    |   35 +-
 .../api/internal/project/ServiceRegistry.java      |   36 +-
 .../project/TaskInternalServiceRegistry.java       |    3 +-
 .../project/TopLevelBuildServiceRegistry.java      |   66 +-
 .../NestedBeanPropertyAnnotationHandler.java       |    1 -
 .../api/internal/tasks/CommandLineOption.java      |   40 +
 .../api/internal/tasks/DefaultTaskContainer.java   |    2 +-
 .../tasks/DefaultTaskContainerFactory.java         |   37 +
 .../api/internal/tasks/DefaultTaskExecuter.java    |    3 +
 .../api/internal/tasks/DefaultTaskInputs.java      |    5 +
 .../api/internal/tasks/DefaultTaskOutputs.java     |    5 +
 .../api/internal/tasks/TaskStateInternal.java      |    2 +-
 .../groovy/org/gradle/api/logging/LogLevel.java    |    2 +
 .../groovy/org/gradle/api/logging/Logging.java     |   12 +-
 .../org/gradle/api/logging/LoggingOutput.java      |    4 +-
 .../org/gradle/api/plugins/PluginCollection.java   |    1 +
 .../api/plugins/PluginInstantiationException.java  |    2 +
 .../gradle/api/plugins/UnknownPluginException.java |    2 +
 .../main/groovy/org/gradle/api/specs/AndSpec.java  |    4 +
 .../groovy/org/gradle/api/specs/CompositeSpec.java |    3 +
 .../main/groovy/org/gradle/api/specs/NotSpec.java  |    3 +
 .../main/groovy/org/gradle/api/specs/OrSpec.java   |    6 +-
 .../src/main/groovy/org/gradle/api/specs/Spec.java |    3 +
 .../main/groovy/org/gradle/api/specs/Specs.java    |    2 +
 .../org/gradle/api/tasks/AbstractCopyTask.java     |    2 +-
 .../src/main/groovy/org/gradle/api/tasks/Copy.java |    2 +-
 .../main/groovy/org/gradle/api/tasks/Delete.java   |    2 +-
 .../groovy/org/gradle/api/tasks/Directory.groovy   |    2 +
 .../src/main/groovy/org/gradle/api/tasks/Exec.java |    2 +-
 .../groovy/org/gradle/api/tasks/GradleBuild.java   |    4 +-
 .../main/groovy/org/gradle/api/tasks/JavaExec.java |    2 +-
 .../src/main/groovy/org/gradle/api/tasks/Sync.java |    3 +-
 .../org/gradle/api/tasks/TaskCollection.java       |    2 +
 .../groovy/org/gradle/api/tasks/TaskInputs.java    |    8 +
 .../groovy/org/gradle/api/tasks/TaskOutputs.java   |   10 +-
 .../main/groovy/org/gradle/api/tasks/Upload.java   |    3 +-
 .../groovy/org/gradle/api/tasks/WorkResult.java    |    3 +
 .../api/tasks/bundling/AbstractArchiveTask.java    |    2 +-
 .../org/gradle/api/tasks/bundling/Compression.java |    2 +
 .../org/gradle/api/tasks/bundling/LongFile.java    |   37 -
 .../groovy/org/gradle/api/tasks/bundling/Tar.java  |   21 +-
 .../groovy/org/gradle/api/tasks/bundling/Zip.java  |    2 +
 .../api/tasks/diagnostics/AbstractReportTask.java  |   11 +-
 .../api/tasks/diagnostics/AsciiReportRenderer.java |  194 ---
 .../diagnostics/DependencyReportRenderer.java      |   47 -
 .../tasks/diagnostics/DependencyReportTask.java    |    7 +-
 .../tasks/diagnostics/GraphvizReportRenderer.java  |   75 --
 .../tasks/diagnostics/ProjectReportRenderer.java   |   52 -
 .../api/tasks/diagnostics/ProjectReportTask.java   |  103 ++
 .../tasks/diagnostics/PropertyReportRenderer.java  |   39 -
 .../api/tasks/diagnostics/PropertyReportTask.java  |    7 +-
 .../gradle/api/tasks/diagnostics/TaskDetails.java  |   28 -
 .../api/tasks/diagnostics/TaskReportModel.java     |  121 --
 .../api/tasks/diagnostics/TaskReportRenderer.java  |  147 ---
 .../api/tasks/diagnostics/TaskReportTask.java      |   33 +-
 .../diagnostics/TextProjectReportRenderer.java     |   86 --
 .../AggregateMultiProjectTaskReportModel.java      |   87 ++
 .../diagnostics/internal/AsciiReportRenderer.java  |  149 +++
 .../internal/DefaultGroupTaskReportModel.java      |   82 ++
 .../internal/DependencyReportRenderer.java         |   47 +
 .../tasks/diagnostics/internal/GraphRenderer.java  |   65 +
 .../internal/GraphvizReportRenderer.java           |   75 ++
 .../internal/PropertyReportRenderer.java           |   32 +
 .../tasks/diagnostics/internal/ReportRenderer.java |   60 +
 .../internal/SingleProjectTaskReportModel.java     |  132 ++
 .../tasks/diagnostics/internal/TaskDetails.java    |   30 +
 .../diagnostics/internal/TaskDetailsFactory.java   |   60 +
 .../diagnostics/internal/TaskReportModel.java      |   32 +
 .../diagnostics/internal/TaskReportRenderer.java   |  150 +++
 .../diagnostics/internal/TextReportRenderer.java   |  100 ++
 .../org/gradle/api/tasks/util/PatternSet.groovy    |    1 +
 .../gradle/api/tasks/util/RelativePathSpec.java    |   32 -
 .../gradle/configuration/BuildConfigurer.groovy    |   52 -
 .../org/gradle/configuration/BuildConfigurer.java  |   22 +
 .../configuration/DefaultBuildConfigurer.java      |   43 +
 .../configuration/DefaultScriptPluginFactory.java  |    6 +-
 .../configuration/GradleLauncherMetaData.java      |   35 +
 .../main/groovy/org/gradle/configuration/Help.java |   47 +
 .../configuration/ImplicitTasksConfigurer.java     |   58 +
 .../ProjectDependencies2TaskResolver.java          |   27 +-
 .../configuration/ProjectEvaluationConfigurer.java |   25 +
 .../gradle/execution/BuiltInTaskBuildExecuter.java |   75 --
 .../org/gradle/execution/DefaultBuildExecuter.java |    7 +-
 .../execution/DependencyReportBuildExecuter.java   |   33 -
 .../execution/ProjectDefaultsBuildExecuter.java    |   10 +-
 .../execution/PropertyReportBuildExecuter.java     |   33 -
 .../org/gradle/execution/TaskNameResolver.java     |   73 ++
 .../execution/TaskNameResolvingBuildExecuter.java  |  150 +--
 .../gradle/execution/TaskReportBuildExecuter.java  |   41 -
 .../groovy/org/gradle/execution/TaskSelector.java  |  118 ++
 .../AbstractCommandLineConverter.java              |   26 +
 .../gradle/initialization/BuildProgressLogger.java |    2 +-
 .../gradle/initialization/BuildSourceBuilder.java  |    1 +
 .../CommandLine2StartParameterConverter.java       |   31 -
 .../initialization/CommandLineConverter.java       |   29 +
 .../gradle/initialization/CommandLineOption.java   |   72 ++
 .../gradle/initialization/CommandLineParser.java   |  399 ++++++
 .../initialization/DefaultClassLoaderFactory.java  |   10 +-
 ...DefaultCommandLine2StartParameterConverter.java |  415 -------
 .../DefaultCommandLineConverter.java               |  250 ++++
 .../initialization/DefaultGradleLauncher.java      |    2 +-
 .../DefaultGradleLauncherFactory.java              |   56 +-
 .../initialization/DefaultProjectDescriptor.java   |   26 +-
 .../DefaultProjectDescriptorRegistry.java          |    5 +-
 .../initialization/IProjectDescriptorRegistry.java |    3 +-
 .../gradle/initialization/ParsedCommandLine.java   |   70 ++
 .../initialization/ParsedCommandLineOption.java    |   50 +
 .../ScriptEvaluatingSettingsProcessor.java         |    4 -
 .../org/gradle/invocation/DefaultGradle.java       |    7 +-
 .../org/gradle/listener/ListenerManager.java       |    2 +-
 .../AbstractProgressLoggingAwareFormatter.java     |   93 --
 .../groovy/org/gradle/logging/AnsiConsole.java     |  296 -----
 .../BasicProgressLoggingAwareFormatter.java        |  101 --
 .../main/groovy/org/gradle/logging/Console.java    |   23 -
 .../org/gradle/logging/ConsoleBackedFormatter.java |  107 --
 .../gradle/logging/DefaultLoggingConfigurer.java   |   37 -
 .../org/gradle/logging/DefaultLoggingManager.java  |  192 ---
 .../logging/DefaultLoggingManagerFactory.java      |   37 -
 .../logging/DefaultProgressLoggerFactory.java      |   84 --
 .../logging/DefaultStandardOutputRedirector.java   |   88 --
 .../org/gradle/logging/IvyLoggingAdaper.java       |   47 -
 .../gradle/logging/JavaUtilLoggingConfigurer.java  |   38 -
 .../src/main/groovy/org/gradle/logging/Label.java  |   23 -
 .../org/gradle/logging/LayoutBasedFormatter.java   |   35 -
 .../org/gradle/logging/LogEventFormatter.java      |   23 -
 .../org/gradle/logging/LoggingConfiguration.java   |   39 +
 .../org/gradle/logging/LoggingConfigurer.java      |   25 -
 .../org/gradle/logging/LoggingManagerFactory.java  |   21 -
 .../org/gradle/logging/LoggingManagerInternal.java |    8 +-
 .../org/gradle/logging/LoggingServiceRegistry.java |   49 +-
 .../groovy/org/gradle/logging/LoggingSystem.java   |   43 -
 .../org/gradle/logging/LoggingSystemAdapter.java   |   66 -
 .../groovy/org/gradle/logging/MarkerFilter.java    |   67 -
 .../OutputStreamStandardOutputListenerAdapter.java |   42 -
 .../gradle/logging/PrintStreamLoggingSystem.java   |  128 --
 .../org/gradle/logging/ProgressListener.java       |   25 -
 .../groovy/org/gradle/logging/ProgressLogger.java  |    8 +-
 .../org/gradle/logging/ProgressLoggerFactory.java  |   19 +-
 .../org/gradle/logging/ProgressLoggingBridge.java  |   36 -
 .../org/gradle/logging/Slf4jLoggingConfigurer.java |  244 ----
 .../org/gradle/logging/StdErrLoggingSystem.java    |   36 -
 .../org/gradle/logging/StdOutLoggingSystem.java    |   36 -
 .../org/gradle/logging/StyledTextOutput.java       |  160 +++
 .../gradle/logging/StyledTextOutputFactory.java    |   55 +
 .../org/gradle/logging/TerminalDetector.java       |   43 -
 .../main/groovy/org/gradle/logging/TextArea.java   |   21 -
 .../logging/internal/AbstractStyledTextOutput.java |  167 +++
 .../internal/AbstractStyledTextOutputFactory.java  |   34 +
 .../org/gradle/logging/internal/AnsiConsole.java   |  306 +++++
 .../logging/internal/CategorisedOutputEvent.java   |   42 +
 .../org/gradle/logging/internal/ColorMap.java      |   31 +
 .../org/gradle/logging/internal/Console.java       |   23 +
 .../internal/ConsoleBackedProgressRenderer.java    |   63 +
 .../gradle/logging/internal/DefaultColorMap.java   |  153 +++
 .../logging/internal/DefaultLoggingConfigurer.java |   37 +
 .../logging/internal/DefaultLoggingManager.java    |  204 ++++
 .../internal/DefaultLoggingManagerFactory.java     |   38 +
 .../internal/DefaultProgressLoggerFactory.java     |   91 ++
 .../internal/DefaultStandardOutputRedirector.java  |   90 ++
 .../internal/DefaultStyledTextOutputFactory.java   |   35 +
 .../internal/JavaUtilLoggingConfigurer.java        |   41 +
 .../groovy/org/gradle/logging/internal/Label.java  |   23 +
 .../org/gradle/logging/internal/LogEvent.java      |   51 +
 .../logging/internal/LogLevelChangeEvent.java      |   40 +
 .../internal/LoggingBackedStyledTextOutput.java    |   99 ++
 .../internal/LoggingCommandLineConverter.java      |  116 ++
 .../gradle/logging/internal/LoggingConfigurer.java |   25 +
 .../logging/internal/LoggingOutputInternal.java    |   26 +
 .../org/gradle/logging/internal/LoggingSystem.java |   43 +
 .../logging/internal/LoggingSystemAdapter.java     |   66 +
 .../org/gradle/logging/internal/MarkerFilter.java  |   67 +
 .../org/gradle/logging/internal/OutputEvent.java   |   30 +
 .../logging/internal/OutputEventListener.java      |   20 +
 .../logging/internal/OutputEventRenderer.java      |  187 +++
 .../logging/internal/PrintStreamLoggingSystem.java |  140 +++
 .../logging/internal/ProgressCompleteEvent.java    |   36 +
 .../org/gradle/logging/internal/ProgressEvent.java |   36 +
 .../gradle/logging/internal/ProgressListener.java  |   25 +
 .../internal/ProgressLogEventGenerator.java        |  183 +++
 .../logging/internal/ProgressLoggingBridge.java    |   37 +
 .../logging/internal/ProgressStartEvent.java       |   36 +
 .../logging/internal/RenderableOutputEvent.java    |   35 +
 .../logging/internal/Slf4jLoggingConfigurer.java   |  183 +++
 .../logging/internal/StdErrLoggingSystem.java      |   36 +
 .../logging/internal/StdOutLoggingSystem.java      |   36 +
 .../internal/StreamingStyledTextOutput.java        |   74 ++
 .../internal/StreamingStyledTextOutputFactory.java |   31 +
 .../internal/StyledTextOutputBackedRenderer.java   |   81 ++
 .../logging/internal/StyledTextOutputEvent.java    |  104 ++
 .../gradle/logging/internal/TerminalDetector.java  |   54 +
 .../org/gradle/logging/internal/TextArea.java      |   22 +
 .../internal/TextStreamOutputEventListener.java    |   55 +
 .../gradle/messaging/remote/internal/Message.java  |   48 +-
 .../groovy/org/gradle/process/BaseExecSpec.java    |   11 +-
 .../main/groovy/org/gradle/process/ExecSpec.java   |    4 +-
 .../groovy/org/gradle/process/JavaExecSpec.java    |    2 +
 .../internal/DefaultWorkerProcessFactory.java      |    5 +-
 .../gradle/process/internal/ExecHandleRunner.java  |    8 +-
 .../process/internal/ExecOutputHandleRunner.java   |    1 +
 .../process/internal/ProcessBuilderFactory.java    |    7 +-
 .../process/internal/WorkerProcessFactory.java     |   20 -
 .../child/ImplementationClassLoaderWorker.java     |    3 +-
 .../groovy/org/gradle/process/package-info.java    |   20 +
 .../groovy/org/gradle/profile/BuildProfile.java    |  209 ++++
 .../org/gradle/profile/ElapsedTimeFormatter.java   |   54 +
 .../org/gradle/profile/HTMLProfileReport.groovy    |   55 +
 .../groovy/org/gradle/profile/ProfileListener.java |  104 ++
 .../groovy/org/gradle/profile/ProjectProfile.java  |  115 ++
 .../groovy/org/gradle/profile/TaskProfile.java     |   74 ++
 .../org/gradle/testfixtures/ProjectBuilder.java    |   33 +-
 .../org/gradle/util/BulkReadInputStream.java       |   38 +
 .../org/gradle/util/DisconnectableInputStream.java |  153 +++
 .../src/main/groovy/org/gradle/util/GUtil.java     |   66 +-
 .../main/groovy/org/gradle/util/GradleVersion.java |   22 +-
 .../src/main/groovy/org/gradle/util/HashUtil.java  |    4 +-
 .../org/gradle/util/IgnoreInterruptHandler.java    |   26 -
 .../groovy/org/gradle/util/InterruptHandler.java   |   24 -
 .../main/groovy/org/gradle/util/JavaMethod.java    |    6 +
 .../src/main/groovy/org/gradle/util/Jvm.java       |    5 +
 .../org/gradle/util/LineBufferingOutputStream.java |    6 +-
 .../util/LinePerThreadBufferingOutputStream.java   |   12 +
 .../groovy/org/gradle/util/OperatingSystem.java    |    5 +
 .../src/main/groovy/org/gradle/util/Path.java      |  167 +++
 .../main/groovy/org/gradle/util/PathHelper.java    |   32 -
 .../org/gradle/profile/ProfileTemplate.html        |  130 ++
 .../org/gradle/BuildExceptionReporterTest.groovy   |  251 ++++
 .../org/gradle/BuildExceptionReporterTest.java     |  177 ---
 .../groovy/org/gradle/BuildResultLoggerTest.java   |   40 +-
 .../groovy/org/gradle/StartParameterTest.groovy    |    7 +-
 .../groovy/org/gradle/TaskExecutionLoggerTest.java |    6 +-
 .../ProjectDependenciesBuildInstructionTest.java   |   78 --
 .../api/artifacts/specs/DependencySpecsTest.java   |   43 +
 .../artifacts/specs/DependencyTypeSpecTest.java    |   46 -
 .../gradle/api/internal/XmlTransformerTest.groovy  |  180 +++
 .../ProjectDependenciesBuildInstructionTest.java   |   80 ++
 .../DefaultConfigurationContainerTest.java         |    2 +-
 .../dependencies/DefaultProjectDependencyTest.java |    1 +
 .../dsl/DefaultRepositoryHandlerFactoryTest.java   |   31 +-
 .../DefaultProjectDependencyFactoryTest.java       |    2 +-
 .../ivyservice/DefaultIvyServicePublishTest.java   |    2 +-
 .../ivyservice/DefaultIvyServiceResolveTest.java   |    2 +-
 .../ProjectDependencyDescriptorFactoryTest.java    |    2 +-
 .../maven/DefaultMavenPomFactoryTest.groovy        |    2 +-
 .../publish/maven/DeployTaskFactoryTest.java       |    2 +-
 .../maven/deploy/BaseMavenDeployerTest.java        |    5 +-
 .../maven/deploy/BaseMavenInstallerTest.java       |    7 +-
 .../maven/deploy/BasePomFilterContainerTest.java   |   14 +-
 .../groovy/DefaultGroovyMavenDeployerTest.groovy   |   10 +-
 .../groovy/DefaultGroovyMavenInstallerTest.groovy  |   85 --
 .../groovy/DefaultGroovyMavenUploaderTest.groovy   |    3 +-
 .../gradle/api/internal/file/RelativePathTest.java |    2 +-
 .../api/internal/file/copy/CopySpecImplTest.groovy |    9 +
 .../file/copy/MappingCopySpecVisitorTest.java      |    1 -
 .../file/copy/SyncCopySpecVisitorTest.java         |    1 -
 .../DefaultScriptHandlerFactoryTest.groovy         |   14 +-
 .../project/DefaultAntBuilderFactoryTest.groovy    |    4 +-
 .../api/internal/project/DefaultProjectTest.groovy |   63 +-
 .../project/DefaultServiceRegistryTest.java        |  101 +-
 .../project/GlobalServicesRegistryTest.java        |   12 +-
 .../project/GradleInternalServiceRegistryTest.java |   18 +-
 .../api/internal/project/ProjectFactoryTest.java   |   10 +-
 .../ProjectInternalServiceRegistryTest.java        |   63 +-
 .../project/TaskInternalServiceRegistryTest.java   |   11 +-
 .../project/TopLevelBuildServiceRegistryTest.java  |   29 +-
 .../groovy/org/gradle/api/tasks/DeleteTest.java    |    2 +-
 .../tasks/diagnostics/AbstractReportTaskTest.java  |   23 +-
 .../tasks/diagnostics/AsciiReportRendererTest.java |   65 -
 .../diagnostics/DependencyReportTaskTest.java      |    4 +-
 .../tasks/diagnostics/ProjectReportTaskTest.groovy |   92 ++
 .../diagnostics/PropertyReportRendererTest.java    |   41 -
 .../tasks/diagnostics/PropertyReportTaskTest.java  |    3 +-
 .../tasks/diagnostics/TaskReportModelTest.groovy   |  164 ---
 .../diagnostics/TaskReportRendererTest.groovy      |  175 ---
 .../api/tasks/diagnostics/TaskReportTaskTest.java  |   51 +-
 .../diagnostics/TextProjectReportRendererTest.java |  102 --
 ...AggregateMultiProjectTaskReportModelTest.groovy |  115 ++
 .../internal/AsciiReportRendererTest.groovy        |  116 ++
 .../DefaultGroupTaskReportModelTest.groovy         |  110 ++
 .../internal/PropertyReportRendererTest.java       |   36 +
 .../SingleProjectTaskReportModelTest.groovy        |  178 +++
 .../internal/TaskDetailsFactoryTest.groovy         |   74 ++
 .../internal/TaskModelSpecification.groovy         |   49 +
 .../internal/TaskReportRendererTest.groovy         |  184 +++
 .../internal/TextReportRendererTest.java           |  118 ++
 .../gradle/configuration/BuildConfigurerTest.java  |   83 --
 .../DefaultBuildConfigurerTest.groovy              |   40 +
 .../DefaultScriptPluginFactoryTest.java            |    4 +-
 .../GradleLauncherMetaDataTest.groovy              |   47 +
 .../ImplicitTasksConfigurerTest.groovy             |   42 +
 .../ProjectDependencies2TaskResolverTest.groovy    |    2 +-
 .../execution/BuiltInTaskBuildExecuterTest.java    |  158 ---
 .../gradle/execution/DefaultBuildExecuterTest.java |   30 +-
 .../DependencyReportBuildExecuterTest.groovy       |   29 -
 .../ProjectDefaultsBuildExecuterTest.java          |   31 +-
 .../PropertyReportBuildExecuterTest.groovy         |   29 -
 .../gradle/execution/TaskNameResolverTest.groovy   |  162 +++
 .../TaskNameResolvingBuildExecuterTest.java        |  213 ++--
 .../execution/TaskReportBuildExecuterTest.groovy   |   29 -
 .../gradle/initialization/BuildLoaderTest.groovy   |    3 -
 .../initialization/BuildProgressLoggerTest.groovy  |    2 +-
 .../initialization/CommandLineParserTest.groovy    |  540 ++++++++
 ...ultCommandLine2StartParameterConverterTest.java |  397 ------
 .../DefaultCommandLineConverterTest.java           |  356 ++++++
 .../DefaultGradleLauncherFactoryTest.java          |   47 +-
 .../initialization/DefaultGradleLauncherTest.java  |   11 +-
 .../DefaultProjectDescriptorRegistryTest.java      |    3 +-
 .../DefaultProjectDescriptorTest.java              |    4 +-
 .../org/gradle/invocation/DefaultGradleTest.java   |   28 +-
 .../org/gradle/logging/AnsiConsoleTest.groovy      |  298 -----
 .../BasicProgressLoggingAwareFormatterTest.groovy  |  240 ----
 .../logging/ConsoleBackedFormatterTest.groovy      |  278 -----
 .../gradle/logging/DefaultLoggingManagerTest.java  |  364 ------
 .../DefaultProgressLoggerFactoryTest.groovy        |   81 --
 .../DefaultStandardOutputRedirectorTest.groovy     |   90 --
 .../logging/JavaUtilLoggingConfigurerTest.groovy   |   50 -
 .../logging/LoggingServiceRegistryTest.groovy      |   25 +-
 .../gradle/logging/LoggingSystemAdapterTest.groovy |   53 -
 .../logging/PrintStreamLoggingSystemTest.groovy    |  138 ---
 .../gradle/logging/Slf4jLoggingConfigurerTest.java |  311 -----
 .../internal/AbstractStyledTextOutputTest.groovy   |  232 ++++
 .../gradle/logging/internal/AnsiConsoleTest.groovy |  368 ++++++
 .../ConsoleBackedProgressRendererTest.groovy       |  133 ++
 .../org/gradle/logging/internal/ConsoleStub.java   |   41 +
 .../logging/internal/DefaultColorMapTest.groovy    |  106 ++
 .../internal/DefaultLoggingManagerTest.java        |  363 ++++++
 .../DefaultProgressLoggerFactoryTest.groovy        |   78 ++
 .../DefaultStandardOutputRedirectorTest.groovy     |   90 ++
 .../internal/JavaUtilLoggingConfigurerTest.groovy  |   51 +
 .../gradle/logging/internal/LogEventTest.groovy    |   47 +
 .../LoggingBackedStyledTextOutputTest.groovy       |  133 ++
 .../LoggingCommandLineConverterTest.groovy         |   67 +
 .../internal/LoggingSystemAdapterTest.groovy       |   53 +
 .../internal/OutputEventRendererTest.groovy        |  239 ++++
 .../logging/internal/OutputSpecification.groovy    |   70 ++
 .../internal/PrintStreamLoggingSystemTest.groovy   |  159 +++
 .../internal/ProgressLogEventGeneratorTest.groovy  |  443 +++++++
 .../internal/Slf4jLoggingConfigurerTest.groovy     |  180 +++
 .../internal/StreamingStyledTextOutputTest.groovy  |   77 ++
 .../StyledTextOutputBackedRendererTest.groovy      |  105 ++
 .../internal/StyledTextOutputEventTest.groovy      |   64 +
 .../TextStreamOutputEventListenerTest.groovy       |   63 +
 .../messaging/remote/internal/MessageTest.groovy   |  204 ++++
 .../internal/RemoteMethodInvocationTest.java       |  149 +--
 .../internal/DefaultWorkerProcessFactoryTest.java  |    2 +-
 .../util/DisconnectableInputStreamTest.groovy      |  291 +++++
 .../org/gradle/util/GradleVersionTest.groovy       |    8 +-
 .../test/groovy/org/gradle/util/HelperUtil.groovy  |    2 +-
 .../org/gradle/util/JUnit4GroovyMockery.java       |   20 +-
 .../gradle/util/MultiParentClassLoaderTest.groovy  |   10 +-
 .../org/gradle/util/MultithreadedTestCase.java     |    2 +-
 .../groovy/org/gradle/util/PathHelperTest.groovy   |   31 -
 .../test/groovy/org/gradle/util/PathTest.groovy    |  114 ++
 subprojects/gradle-docs/docs.gradle                |   97 +-
 subprojects/gradle-docs/src/docs/css/print.css     |    4 +-
 .../src/docs/stylesheets/userGuideHtmlCommon.xsl   |    4 +-
 .../gradle-docs/src/docs/userguide/antlrPlugin.xml |   49 +-
 .../gradle-docs/src/docs/userguide/commandLine.xml |   42 +-
 .../src/docs/userguide/commandLineTutorial.xml     |   71 +-
 .../gradle-docs/src/docs/userguide/dsl/dsl.xml     |   36 +
 .../userguide/dsl/org.gradle.api.DefaultTask.xml   |   26 +
 .../docs/userguide/dsl/org.gradle.api.Project.xml  |   40 +
 .../dsl/org.gradle.api.internal.AbstractTask.xml   |   26 +
 .../dsl/org.gradle.api.internal.ConventionTask.xml |   26 +
 ...org.gradle.api.plugins.BasePluginConvention.xml |   30 +
 ...e.api.plugins.ReportingBasePluginConvention.xml |   30 +
 .../dsl/org.gradle.api.tasks.AbstractCopyTask.xml  |   39 +
 .../dsl/org.gradle.api.tasks.GroovySourceSet.xml   |   32 +
 .../dsl/org.gradle.api.tasks.SourceSet.xml         |   39 +
 ...adle.api.tasks.bundling.AbstractArchiveTask.xml |   35 +
 .../dsl/org.gradle.api.tasks.bundling.Tar.xml      |   31 +
 .../dsl/org.gradle.api.tasks.bundling.Zip.xml      |   27 +
 .../dsl/org.gradle.plugins.idea.IdeaProject.xml    |   40 +
 .../gradle-docs/src/docs/userguide/dsl/plugins.xml |   13 +
 .../src/docs/userguide/eclipsePlugin.xml           |    4 +-
 .../gradle-docs/src/docs/userguide/ideaPlugin.xml  |    6 +-
 .../gradle-docs/src/docs/userguide/img/profile.png |  Bin 0 -> 168970 bytes
 .../src/docs/userguide/installation.xml            |   25 +-
 .../src/docs/userguide/javaTutorial.xml            |    4 +-
 .../gradle-docs/src/docs/userguide/logging.xml     |   11 +-
 .../gradle-docs/src/docs/userguide/mavenPlugin.xml |    2 +-
 .../src/docs/userguide/projectReports.xml          |    3 +-
 .../src/docs/userguide/standardTasks.xml           |  155 +++
 .../gradle-docs/src/docs/userguide/tasks.xml       |   69 +-
 .../gradle-docs/src/docs/userguide/userguide.xml   |    3 +
 .../src/docs/userguide/workingWithFiles.xml        |   11 +-
 .../src/docs/userguide/writingBuildScripts.xml     |    5 +
 .../src/samples/codeQuality/build.gradle           |    2 +-
 .../samples/groovy/customizedLayout/build.gradle   |    2 +-
 .../samples/groovy/mixedJavaAndGroovy/build.gradle |    2 +-
 .../multiproject/groovycDetector/build.gradle      |    2 +-
 .../groovy/multiproject/testproject/build.gradle   |    2 +-
 .../src/test/groovy/org/gradle/VersionTest.groovy  |    4 +-
 .../src/samples/groovy/quickstart/build.gradle     |    2 +-
 .../src/test/groovy/org/gradle/PersonTest.groovy   |    2 +-
 .../src/samples/logging/project1/build.gradle      |   75 --
 .../gradle-docs/src/samples/osgi/build.gradle      |    2 +-
 .../testng/groovy-jdk15-failing/build.gradle       |   17 -
 .../testng/groovy-jdk15-passing/build.gradle       |   17 -
 .../samples/testng/java-jdk14-failing/build.gradle |   16 -
 .../samples/testng/java-jdk14-passing/build.gradle |    2 +-
 .../samples/testng/java-jdk15-failing/build.gradle |   15 -
 .../java-jdk15-passing-no-report/build.gradle      |   16 -
 .../src/main/java/org/gradle/Ok.java               |    4 -
 .../src/test/java/org/gradle/OkTest.java           |    5 -
 .../samples/testng/java-jdk15-passing/build.gradle |    2 +-
 .../samples/testng/suitexmlbuilder/build.gradle    |    2 +-
 .../artifacts/externalDependencies/build.gradle    |    4 +-
 .../src/samples/userguide/files/sync/build.gradle  |    8 +
 .../incrementalBuild/inputsAndOutputs/build.gradle |   21 +
 .../inputsAndOutputs/mountains.xml                 |   10 +
 .../noInputsAndOutputs/build.gradle                |   15 +
 .../noInputsAndOutputs/mountains.xml               |   10 +
 .../userguide/tutorial/logging/build.gradle        |   35 +
 .../userguide/tutorial/projectReports/build.gradle |   47 +-
 .../src/samples/userguide/wrapper/build.gradle     |    2 +-
 .../userguideOutput/dependencyListReport.out       |   13 +-
 .../src/samples/userguideOutput/generatorTask.out  |    2 +
 .../samples/userguideOutput/incrementalTask.out    |    1 +
 .../samples/userguideOutput/projectListReport.out  |    7 +
 .../samples/userguideOutput/propertyListReport.out |    7 +-
 .../samples/userguideOutput/taskListAllReport.out  |   21 +-
 .../src/samples/userguideOutput/taskListReport.out |   16 +-
 .../eclipse/AbstractXmlGeneratorTask.groovy        |    5 -
 .../gradle/plugins/eclipse/EclipseClasspath.groovy |   27 +-
 .../gradle/plugins/eclipse/EclipseProject.groovy   |   33 +-
 .../org/gradle/plugins/eclipse/EclipseWtp.groovy   |   10 +-
 .../gradle/plugins/eclipse/model/Classpath.groovy  |   21 +-
 .../plugins/eclipse/model/ClasspathEntry.java      |    2 +
 .../gradle/plugins/eclipse/model/Project.groovy    |   12 +-
 .../org/gradle/plugins/eclipse/model/Wtp.groovy    |   15 +-
 .../eclipse/model/internal/ClasspathFactory.groovy |    2 +-
 .../gradle/plugins/eclipse/model/package-info.java |   20 +
 .../org/gradle/plugins/eclipse/package-info.java   |    2 +-
 .../plugins/eclipse/model/ClasspathTest.groovy     |   33 +-
 .../plugins/eclipse/model/ProjectTest.groovy       |   10 +-
 .../org/gradle/plugins/idea/IdeaModule.groovy      |   43 +-
 .../org/gradle/plugins/idea/IdeaProject.groovy     |   51 +-
 .../org/gradle/plugins/idea/IdeaWorkspace.groovy   |   31 +-
 .../org/gradle/plugins/idea/model/Dependency.java  |    2 +
 .../org/gradle/plugins/idea/model/Module.groovy    |   16 +-
 .../gradle/plugins/idea/model/ModulePath.groovy    |   15 +-
 .../org/gradle/plugins/idea/model/Path.groovy      |   52 +-
 .../gradle/plugins/idea/model/PathFactory.groovy   |   49 +
 .../org/gradle/plugins/idea/model/Project.groovy   |   18 +-
 .../org/gradle/plugins/idea/model/Workspace.groovy |   11 +-
 .../gradle/plugins/idea/model/package-info.java    |   20 +
 .../org/gradle/plugins/idea/package-info.java      |   20 +
 .../plugins/idea/model/ModulePathTest.groovy       |   28 +
 .../gradle/plugins/idea/model/ModuleTest.groovy    |   25 +-
 .../plugins/idea/model/PathFactoryTest.groovy      |   70 ++
 .../org/gradle/plugins/idea/model/PathTest.groovy  |  113 ++
 .../gradle/plugins/idea/model/ProjectTest.groovy   |   28 +-
 .../gradle/plugins/idea/model/WorkspaceTest.groovy |   11 +-
 subprojects/gradle-jetty/jetty.gradle              |    6 +-
 .../api/plugins/jetty/AbstractJettyRunTask.java    |   23 +-
 .../api/plugins/jetty/AbstractJettyRunWarTask.java |   48 -
 .../api/plugins/jetty/JettyPluginConvention.java   |    2 +
 .../org/gradle/api/plugins/jetty/JettyRun.java     |   24 +-
 .../org/gradle/api/plugins/jetty/JettyRunWar.java  |   27 +-
 .../org/gradle/api/plugins/jetty/JettyStop.java    |   10 +-
 .../api/plugins/jetty/ScanTargetPattern.java       |    9 +-
 .../java/org/gradle/launcher/BuildCompleter.java   |   20 +
 .../gradle/launcher/CommandLineActionFactory.java  |  195 +++
 .../main/java/org/gradle/launcher/GradleMain.java  |   17 +-
 .../src/main/java/org/gradle/launcher/Main.java    |   95 +-
 .../launcher/CommandLineActionFactoryTest.groovy   |  182 +++
 .../groovy/org/gradle/launcher/MainTest.groovy     |   87 ++
 .../test/groovy/org/gradle/launcher/MainTest.java  |  166 ---
 .../gradle/api/plugins/MavenPluginConvention.java  |    2 +
 subprojects/gradle-open-api/open-api.gradle        |   39 +-
 .../org/gradle/integtests/GradleRunnerTest.groovy  |  277 -----
 .../org/gradle/integtests/OpenApiUiTest.groovy     | 1288 --------------------
 .../org/gradle/integtests/OutputUILordTest.groovy  |  212 ----
 .../TestAlternateUIInteractionVersion1.java        |   54 -
 .../integtests/TestSettingsNodeVersion1.java       |  247 ----
 .../TestSingleDualPaneUIInteractionVersion1.java   |   45 -
 .../openapi/BlockingRequestObserver.java           |  136 +++
 .../openapi/CompatibilityIntegrationTest.groovy    |   68 ++
 .../integtests/openapi/CrossVersionBuilder.java    |   93 ++
 .../integtests/openapi/GradleRunnerTest.groovy     |  262 ++++
 .../gradle/integtests/openapi/OpenApiFixture.java  |  163 +++
 .../gradle/integtests/openapi/OpenApiUiTest.groovy |  986 +++++++++++++++
 .../integtests/openapi/OutputUILordTest.groovy     |  130 ++
 .../TestAlternateUIInteractionVersion1.java        |   54 +
 .../openapi/TestSettingsNodeVersion1.java          |  247 ++++
 .../TestSingleDualPaneUIInteractionVersion1.java   |   45 +
 .../shared/build.gradle                            |    3 +
 .../shared/settings.gradle                         |    1 +
 .../integtests/openapi/testproject/build.gradle    |    1 +
 .../integtests/openapi/testproject/settings.gradle |    1 +
 .../runner/GradleRunnerInteractionVersion1.java    |    4 +-
 .../external/ui/GradleUIInteractionVersion1.java   |    4 +-
 .../api/internal/plugins/osgi/AnalyzerFactory.java |   23 -
 .../plugins/osgi/DefaultAnalyzerFactory.java       |    6 +-
 .../internal/plugins/osgi/DefaultOsgiManifest.java |    9 +-
 .../org/gradle/api/plugins/osgi/OsgiManifest.java  |    8 +-
 .../plugins/osgi/DefaultAnalyzerFactoryTest.java   |    2 +-
 .../plugins/osgi/DefaultOsgiManifestTest.java      |    7 +-
 subprojects/gradle-plugins/plugins.gradle          |    4 +
 .../api/internal/plugins/ProcessResources.java     |   30 +
 .../api/internal/tasks/compile/AntDepend.java      |   37 +
 .../compile/AntDependsStaleClassCleaner.groovy     |    9 +-
 .../internal/tasks/compile/AntJavaCompiler.groovy  |   13 +-
 .../tasks/compile/IncrementalJavaCompiler.java     |    9 +-
 .../tasks/testing/TestClassProcessorFactory.java   |   20 -
 .../detection/ClassFileExtractionManager.java      |    2 -
 .../testing/detection/DefaultTestExecuter.java     |   16 +-
 .../testing/detection/JarFilePackageListener.java  |   24 +
 .../testing/detection/JarFilePackageLister.java    |   71 ++
 .../CaptureTestOutputTestResultProcessor.java      |   70 --
 .../tasks/testing/junit/JULRedirector.java         |    2 +-
 .../testing/junit/JUnitTestClassExecuter.java      |   22 +-
 .../testing/junit/JUnitTestClassProcessor.java     |    1 +
 .../CaptureTestOutputTestResultProcessor.java      |   74 ++
 .../processors/MaxNParallelTestClassProcessor.java |    6 +-
 .../RestartEveryNTestClassProcessor.java           |    6 +-
 .../internal/tasks/testing/results/TestLogger.java |   19 +-
 .../testing/testng/TestNGTestClassProcessor.java   |    2 +-
 .../testng/TestNGTestResultProcessorAdapter.java   |   11 +
 .../testing/worker/ForkingTestClassProcessor.java  |    8 +-
 .../org/gradle/api/java/archives/Manifest.java     |    4 +
 .../api/java/archives/ManifestMergeDetails.java    |    5 +-
 .../api/java/archives/ManifestMergeSpec.java       |    3 +
 .../java/archives/internal/DefaultManifest.java    |   15 +-
 .../org/gradle/api/java/archives/package-info.java |   20 +
 .../org/gradle/api/plugins/BasePlugin.groovy       |    8 +-
 .../org/gradle/api/plugins/GroovyPlugin.java       |    2 +-
 .../org/gradle/api/plugins/JavaBasePlugin.java     |    4 +-
 .../groovy/org/gradle/api/plugins/JavaPlugin.java  |   40 +-
 .../gradle/api/plugins/JavaPluginConvention.groovy |    2 +-
 .../org/gradle/api/plugins/ProcessResources.java   |   30 -
 .../api/plugins/ReportingBasePluginConvention.java |    4 +-
 .../groovy/org/gradle/api/tasks/SourceSet.java     |    4 +-
 .../org/gradle/api/tasks/bundling/Jar.groovy       |   13 +-
 .../org/gradle/api/tasks/bundling/War.groovy       |    2 +
 .../gradle/api/tasks/compile/AbstractCompile.java  |    3 +
 .../org/gradle/api/tasks/compile/AntDepend.java    |   37 -
 .../org/gradle/api/tasks/compile/Compile.java      |    9 +-
 .../gradle/api/tasks/compile/GroovyCompile.java    |    3 +-
 .../org/gradle/api/tasks/javadoc/Groovydoc.java    |   10 +-
 .../org/gradle/api/tasks/javadoc/Javadoc.java      |    4 +-
 .../groovy/org/gradle/api/tasks/testing/Test.java  |   12 +-
 .../gradle/api/tasks/testing/TestDescriptor.java   |    4 +-
 .../org/gradle/api/tasks/testing/TestResult.java   |   16 +-
 .../external/javadoc/CoreJavadocOptions.java       |   19 +-
 .../external/javadoc/JavadocExecHandleBuilder.java |   90 --
 .../external/javadoc/JavadocOptionFileOption.java  |   27 +
 .../external/javadoc/MinimalJavadocOptions.java    |    2 +
 .../javadoc/OptionLessJavadocOptionFileOption.java |   35 +
 .../javadoc/StandardJavadocDocletOptions.java      |   30 +-
 .../internal/AbstractJavadocOptionFileOption.java  |   55 +
 .../AbstractListJavadocOptionFileOption.java       |   62 +
 .../internal/BooleanJavadocOptionFileOption.java   |   40 +
 .../internal/EnumJavadocOptionFileOption.java      |   55 +
 .../internal/FileJavadocOptionFileOption.java      |   41 +
 .../internal/GroupsJavadocOptionFileOption.java    |   61 +
 .../javadoc/internal/JavadocExecHandleBuilder.java |   91 ++
 .../javadoc/internal/JavadocOptionFile.java        |  118 ++
 .../javadoc/internal/JavadocOptionFileWriter.java  |   57 +
 .../internal/JavadocOptionFileWriterContext.java   |  112 ++
 .../LinksOfflineJavadocOptionFileOption.java       |   40 +
 .../MultilineStringsJavadocOptionFileOption.java   |   44 +
 .../OptionLessStringsJavadocOptionFileOption.java  |   60 +
 .../internal/PathJavadocOptionFileOption.java      |   40 +
 .../internal/StringJavadocOptionFileOption.java    |   38 +
 .../internal/StringsJavadocOptionFileOption.java   |   38 +
 .../AbstractJavadocOptionFileOption.java           |   50 -
 .../AbstractListJavadocOptionFileOption.java       |   58 -
 .../optionfile/BooleanJavadocOptionFileOption.java |   38 -
 .../optionfile/EnumJavadocOptionFileOption.java    |   54 -
 .../optionfile/FileJavadocOptionFileOption.java    |   39 -
 .../optionfile/GroupsJavadocOptionFileOption.java  |   58 -
 .../javadoc/optionfile/JavadocOptionFile.java      |  115 --
 .../optionfile/JavadocOptionFileOption.java        |   28 -
 .../optionfile/JavadocOptionFileWriter.java        |   56 -
 .../optionfile/JavadocOptionFileWriterContext.java |  112 --
 .../LinksOfflineJavadocOptionFileOption.java       |   40 -
 .../MultilineStringsJavadocOptionFileOption.java   |   44 -
 .../OptionLessJavadocOptionFileOption.java         |   30 -
 .../OptionLessStringsJavadocOptionFileOption.java  |   58 -
 .../optionfile/PathJavadocOptionFileOption.java    |   40 -
 .../optionfile/StringJavadocOptionFileOption.java  |   38 -
 .../optionfile/StringsJavadocOptionFileOption.java |   38 -
 .../optionfile/TagsJavadocOptionFileOption.java    |   56 -
 .../org/gradle/external/javadoc/package-info.java  |    2 -
 ...CaptureTestOutputTestResultProcessorTest.groovy |   64 -
 .../junit/JUnitTestClassProcessorTest.groovy       |   26 +-
 ...CaptureTestOutputTestResultProcessorTest.groovy |   64 +
 .../MaxNParallelTestClassProcessorTest.groovy      |   12 +-
 .../RestartEveryNTestClassProcessorTest.java       |    4 +-
 .../tasks/testing/results/TestLoggerTest.groovy    |   21 +-
 .../testng/TestNGTestClassProcessorTest.groovy     |  118 +-
 .../worker/ForkingTestClassProcessorTest.java      |   10 +-
 .../gradle/api/plugins/JavaBasePluginTest.groovy   |    4 +-
 .../org/gradle/api/plugins/JavaPluginTest.groovy   |   13 +-
 .../org/gradle/api/tasks/javadoc/JavadocTest.java  |    2 +-
 .../javadoc/JavadocExecHandleBuilderTest.groovy    |   99 --
 .../javadoc/StandardJavadocDocletOptionsTest.java  |    6 +-
 .../BooleanJavadocOptionFileOptionTest.java        |   66 +
 .../internal/EnumJavadocOptionFileOptionTest.java  |   60 +
 .../internal/FileJavadocOptionFileOptionTest.java  |   60 +
 .../GroupsJavadocOptionFileOptionTest.java         |   70 ++
 .../internal/JavadocExecHandleBuilderTest.groovy   |  100 ++
 .../javadoc/internal/JavadocOptionFileTest.java    |   66 +
 .../JavadocOptionFileWriterContextTest.java        |   96 ++
 .../LinksOfflineJavadocOptionFileOptionTest.java   |   64 +
 ...ultilineStringsJavadocOptionFileOptionTest.java |   81 ++
 ...tionLessStringsJavadocOptionFileOptionTest.java |   61 +
 .../internal/PathJavadocOptionFileOptionTest.java  |   67 +
 .../StringJavadocOptionFileOptionTest.java         |   62 +
 .../StringsJavadocOptionFileOptionTest.java        |   66 +
 .../BooleanJavadocOptionFileOptionTest.java        |   66 -
 .../EnumJavadocOptionFileOptionTest.java           |   60 -
 .../FileJavadocOptionFileOptionTest.java           |   60 -
 .../GroupsJavadocOptionFileOptionTest.java         |   70 --
 .../javadoc/optionfile/JavadocOptionFileTest.java  |   65 -
 .../JavadocOptionFileWriterContextTest.java        |   96 --
 .../LinksOfflineJavadocOptionFileOptionTest.java   |   64 -
 ...ultilineStringsJavadocOptionFileOptionTest.java |   81 --
 ...tionLessStringsJavadocOptionFileOptionTest.java |   61 -
 .../PathJavadocOptionFileOptionTest.java           |   67 -
 .../StringJavadocOptionFileOptionTest.java         |   62 -
 .../StringsJavadocOptionFileOptionTest.java        |   66 -
 .../TagsJavadocOptionFileOptionTest.java           |   67 -
 .../gradle/api/plugins/scala/ScalaPlugin.groovy    |    2 +-
 .../org/gradle/api/tasks/scala/ScalaCompile.java   |    7 +-
 .../api/tasks/scala/ScalaCompileOptions.groovy     |    2 +-
 .../org/gradle/api/tasks/scala/ScalaDoc.java       |    2 +-
 .../org/gradle/api/tasks/scala/package-info.java   |   20 +
 .../api/tasks/scala/ScalaCompileOptionsTest.groovy |    5 +-
 .../integtests/FavoritesIntegrationTest.java       |    0
 .../integtests/LiveOutputIntegrationTest.groovy    |  212 ++++
 ...projectProjectAndTaskListIntegrationTest.groovy |  251 ++++
 .../gradle/foundation/CommandLineAssistant.java    |   52 +-
 .../org/gradle/foundation/ProjectConverter.java    |   45 +-
 .../java/org/gradle/foundation/ProjectView.java    |   35 +-
 .../main/java/org/gradle/foundation/TaskView.java  |   14 +-
 .../gradle/foundation/common/ReorderableList.java  |    2 +-
 .../ipc/basic/ProcessLauncherServer.java           |    7 +-
 .../org/gradle/foundation/ipc/basic/Server.java    |    2 +-
 .../ipc/gradle/AbstractGradleServerProtocol.java   |   25 +-
 .../gradle/foundation/ipc/gradle/IPCUtilities.java |    9 +-
 .../ipc/gradle/TaskListClientProtocol.java         |   41 +-
 .../ipc/gradle/TaskListServerProtocol.java         |    1 -
 .../gradleplugin/foundation/GradlePluginLord.java  |   21 +-
 .../foundation/request/RefreshTaskListRequest.java |    3 +-
 .../swing/common/PreferencesAssistant.java         |   10 +-
 .../swing/generic/TaskTreeComponent.java           |    5 +
 .../userinterface/swing/generic/tabs/SetupTab.java |   11 +-
 .../foundation/CommandLineAssistantTest.groovy     |   79 ++
 .../gradle/foundation/CommandLineParsingTest.java  |   18 +-
 .../gradle/foundation/ProjectConverterTest.groovy  |  106 ++
 .../groovy/org/gradle/foundation/TestUtility.java  |   66 +-
 .../integtests/LiveOutputIntegrationTest.groovy    |  182 ---
 ...projectProjectAndTaskListIntegrationTest.groovy |  249 ----
 subprojects/gradle-ui/ui.gradle                    |   21 +-
 .../java/org/gradle/api/tasks/wrapper/Wrapper.java |   12 +-
 .../api/tasks/wrapper/WrapperScriptGenerator.java  |   96 --
 .../wrapper/internal/WrapperScriptGenerator.java   |   96 ++
 .../gradle/wrapper/SystemPropertiesHandler.java    |    7 +-
 .../src/main/java/org/gradle/wrapper/Wrapper.java  |    8 +-
 .../wrapper/internal/unixWrapperScriptHead.txt     |   63 +
 .../wrapper/internal/unixWrapperScriptTail.txt     |   78 ++
 .../wrapper/internal/windowsWrapperScriptHead.txt  |  101 ++
 .../{ => internal}/windowsWrapperScriptTail.txt    |    0
 .../api/tasks/wrapper/unixWrapperScriptHead.txt    |   63 -
 .../api/tasks/wrapper/unixWrapperScriptTail.txt    |   75 --
 .../api/tasks/wrapper/windowsWrapperScriptHead.txt |  101 --
 .../org/gradle/api/tasks/wrapper/WrapperTest.java  |    1 +
 wrapper/gradle-wrapper.properties                  |    4 +-
 937 files changed, 28929 insertions(+), 18005 deletions(-)

diff --git a/build.gradle b/build.gradle
index 9f8abf7..2ab35b1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-import java.text.SimpleDateFormat
 import java.util.jar.Attributes
 import org.gradle.api.artifacts.repositories.WebdavResolver
+import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
 
 import org.gradle.build.samples.WrapperProjectCreator
+import org.gradle.build.Version
+import org.gradle.build.Install
+import org.gradle.build.Git
 
 /**
  * For building Gradle you usually don't need to specify any properties. Only certain functionality of the Gradle requires
@@ -32,7 +35,7 @@ import org.gradle.build.samples.WrapperProjectCreator
 
 version = new Version(project)
 
-defaultTasks "clean", "assemble"
+defaultTasks 'assemble'
 
 apply plugin: 'java-base'
 
@@ -53,10 +56,9 @@ libraries = [
         commons_lang: 'commons-lang:commons-lang:2.5 at jar',
         dom4j: 'dom4j:dom4j:1.6.1 at jar',
         google_collections: 'com.google.collections:google-collections:1.0 at jar',
-        groovy: 'org.codehaus.groovy:groovy-all:1.7.3 at jar',
-        ivy: 'org.apache.ivy:ivy:2.2.0-rc1 at jar',
+        groovy: 'org.codehaus.groovy:groovy-all:1.7.5 at jar',
+        ivy: 'org.apache.ivy:ivy:2.2.0 at jar',
         jaxen: 'jaxen:jaxen:1.1 at jar',
-        jopt_simple: 'net.sf.jopt-simple:jopt-simple:3.2 at jar',
         slf4j_api: 'org.slf4j:slf4j-api:1.6.1 at jar',
         jcl_to_slf4j: 'org.slf4j:jcl-over-slf4j:1.6.1 at jar',
         jul_to_slf4j: 'org.slf4j:jul-to-slf4j:1.6.1 at jar',
@@ -69,8 +71,8 @@ libraries = [
 
 libraries.spock = ['org.spockframework:spock-core:0.4-groovy-1.7 at jar', libraries.groovy, 'cglib:cglib-nodep:2.2', 'org.objenesis:objenesis:1.2']
 libraries.groovy_depends = [libraries.groovy, libraries.commons_cli]
-libraries.jetty_depends = ["org.mortbay.jetty:jetty:6.1.22 at jar", "org.mortbay.jetty:jetty-util:6.1.22 at jar",
-        "javax.servlet:servlet-api:2.5 at jar"]
+libraries.jetty_depends = ["org.mortbay.jetty:jetty:6.1.25 at jar", "org.mortbay.jetty:jetty-util:6.1.25 at jar",
+        "org.mortbay.jetty:servlet-api:2.5-20081211 at jar"]
 
 allprojects {
     group = 'org.gradle'
@@ -91,7 +93,6 @@ def FIRST_LEVEL_JMOCK = ['org.hamcrest:hamcrest-core:1.1 at jar', 'org.hamcrest:ham
 
 configure(groovyProjects()) {
     apply plugin: 'groovy'
-    apply plugin: 'code-quality'
     apply plugin: 'eclipse'
 
     archivesBaseName = "gradle-${name.replaceAll("\\p{Upper}") { "-${it.toLowerCase()}" } }"
@@ -105,30 +106,35 @@ configure(groovyProjects()) {
         testCompile 'org.jmock:jmock-legacy:2.5.1 at jar', 'org.objenesis:objenesis:1.0', 'cglib:cglib-nodep:2.1_3'
     }
 
+    test {
+        maxParallelForks = guessMaxForks()
+    }
+
+    tasks.withType(Jar).each { jar ->
+        jar.manifest.mainAttributes([
+                (Attributes.Name.IMPLEMENTATION_TITLE.toString()): 'Gradle',
+                (Attributes.Name.IMPLEMENTATION_VERSION.toString()): version,
+        ])
+    }
+}
+
+allprojects {
+    apply plugin: 'code-quality'
+
     checkstyleConfigDir = "$rootDir/config/checkstyle"
     checkstyleConfigFileName = new File(checkstyleConfigDir, "checkstyle.xml")
     codeNarcConfigFileName = "$rootDir/config/codenarc.xml"
     checkstyleProperties.checkstyleConfigDir = checkstyleConfigDir
-
+}
+configure(groovyProjects()) {
     sourceSets.allObjects { sourceSet ->
         task "${sourceSet.getTaskName('checkstyle', 'groovy')}"(type: Checkstyle) {
             configFile = new File(checkstyleConfigDir, "checkstyle-groovy.xml")
             source sourceSet.allGroovy
             classpath = sourceSet.compileClasspath
-            resultFile = new File(checkstyleResultsDir, "${sourceSet.name}.xml")
+            resultFile = new File(checkstyleResultsDir, "${sourceSet.name}-groovy.xml")
         }
     }
-
-    test {
-        maxParallelForks = guessMaxForks()
-    }
-
-    tasks.withType(Jar).each { jar ->
-        jar.manifest.mainAttributes([
-                (Attributes.Name.IMPLEMENTATION_TITLE.toString()): 'Gradle',
-                (Attributes.Name.IMPLEMENTATION_VERSION.toString()): version,
-        ])
-    }
 }
 
 allprojects {
@@ -148,17 +154,57 @@ allprojects {
         outputDir = "$rootProject.projectDir/intellij/out" as File
         testOutputDir = "$rootProject.projectDir/intellij/testOut" as File
     }
+    eclipseClasspath {
+        variables = [:]
+    }
 }
 
 ideaModule {
     excludeDirs += file('intTestHomeDir')
     excludeDirs += file('buildSrc/build')
     excludeDirs += file('buildSrc/.gradle')
+    excludeDirs += file('performanceTest/.gradle')
+    excludeDirs += file('performanceTest/build')
+    excludeDirs += file('website/.gradle')
+    excludeDirs += file('website/build')
 }
 
 ideaProject {
     wildcards += ['?*.gradle']
     javaVersion = '1.6'
+    withXml { node ->
+        // Use git
+        def vcsConfig = node.component.find { it.'@name' == 'VcsDirectoryMappings' }
+        vcsConfig.mapping[0].'@vcs' = 'Git'
+
+        // Set gradle home
+        def gradleSettings = node.appendNode('component', [name: 'GradleSettings'])
+        gradleSettings.appendNode('option', [name: 'SDK_HOME', value: gradle.gradleHomeDir.absolutePath])
+    }
+
+    whenConfigured { project ->
+        project.jdk.languageLevel = 'JDK_1_5'
+    }
+}
+
+// Exclude resource directories from compilation and add them back in as classpath resources
+
+ideaProject {
+    withXml { node ->
+        def compilerConfig = node.component.find { it.'@name' == 'CompilerConfiguration' }
+        def exclude = compilerConfig.appendNode('excludeFromCompile')
+        Collection resourceDirs = groovyProjects().collect { project -> project.sourceSets*.resources*.srcDirs }.flatten()
+        resourceDirs.each {
+            exclude.appendNode('directory', [url: "file://\$PROJECT_DIR\$/${rootProject.relativePath(it)}", includeSubdirectories: true])
+        }
+    }
+}
+
+configure(groovyProjects()) {
+    ideaModule {
+        scopes.RUNTIME.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files { sourceSets.main.resources.srcDirs })))
+        scopes.TEST.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files { sourceSets*.resources*.srcDirs })))
+    }
 }
 
 configurations {
@@ -186,6 +232,7 @@ evaluationDependsOn(':core')
 clean.dependsOn subprojects.collect { "$it.path:clean" }
 
 task check(overwrite: true, dependsOn: groovyProjects()*.check)
+check.dependsOn ':docs:checkstyleApi'
 task test(overwrite: true, dependsOn: groovyProjects()*.test)
 
 zipRootFolder = "$archivesBaseName-${-> version}"
@@ -245,6 +292,7 @@ task srcZip(type: Zip) {
                 spec.include "$it/src/"
             }
             include 'config/'
+            include 'gradle/'
             include 'src/'
             include '*.gradle'
             include 'gradle.properties'
@@ -273,7 +321,6 @@ task intTestImage(type: Sync) {
 
 task integTest(type: Test, dependsOn: [intTestImage, binZip, allZip, srcZip, ':docs:userguideDocbook']) {
     integTestUserDir = file('intTestHomeDir')
-    systemProperties['integTest.srcDir'] = file('src').absolutePath
     systemProperties['integTest.userGuideInfoDir'] = project(':docs').docbookSrc
     systemProperties['integTest.userGuideOutputDir'] = new File(project(':docs').samplesSrcDir, "userguideOutput").absolutePath
     systemProperties['integTest.gradleUserHomeDir'] = integTestUserDir.absolutePath
@@ -293,7 +340,7 @@ task integTest(type: Test, dependsOn: [intTestImage, binZip, allZip, srcZip, ':d
         systemProperties['integTest.gradleHomeDir'] = intTestImage.integTestGradleHome.absolutePath
         def forkArgs
         if (noForkIntegTests()) {
-            systemProperties['org.gradle.integtest.nofork'] = "true"
+            systemProperties['org.gradle.integtest.fork'] = "false"
             jvmArgs '-Xmx512m', '-XX:MaxPermSize=256m', '-XX:+HeapDumpOnOutOfMemoryError'
         } else {
             jvmArgs '-Xmx512m', '-XX:+HeapDumpOnOutOfMemoryError'
@@ -317,17 +364,21 @@ def guessMaxForks() {
     return Math.max(2, (int) (processors / 2))
 }
 
-task testedDists(dependsOn: [assemble, check, integTest])
+task testedDists(dependsOn: [assemble, check, integTest, 'openApi:integTest', ':ui:integTest'])
 
 task nightlyBuild(dependsOn: [clean, testedDists, ':docs:uploadDocs'])
 
-task install(type: Install, description: 'Installs the minimal distribution into directory $gradle_installPath') {
+task install(type: Install) {
+    description = 'Installs the minimal distribution into directory $gradle_installPath'
+    group = 'build'
     dependsOn binZip.taskDependencies
     with binDistImage
     installDirProperyName = 'gradle_installPath'
 }
 
-task installAll(type: Install, description: 'Installs the full distribution into directory $gradle_installPath') {
+task installAll(type: Install) {
+    description = 'Installs the full distribution into directory $gradle_installPath'
+    group = 'build'
     dependsOn allZip.taskDependencies
     with allDistImage
     installDirProperyName = 'gradle_installPath'
@@ -354,20 +405,44 @@ gradle.taskGraph.whenReady {graph ->
     }
 }
 
-task developerBuild(dependsOn: [clean, testedDists])
-task ciBuild(dependsOn: [clean, testedDists])
+task developerBuild {
+    description = 'Builds distributions and runs pre-checkin checks'
+    group = 'build'
+    dependsOn clean, testedDists
+}
+
+task ciBuild {
+    description = 'Build performed by the CI server'
+    dependsOn clean, testedDists
+}
+gradle.taskGraph.whenReady {graph ->
+    if (graph.hasTask(ciBuild)) {
+        subprojects { reportsDirName = "$rootProject.reportsDir/${path.replaceFirst(':', '').replaceAll(':', '.')}" }
+    }
+}
+
+// A marker task which causes the release version to be used when it is present in the task graph
+task releaseVersion
+
+task tag {
+    doLast {
+        def git = new Git(project)
+        git.checkNoModifications()
+        git.tag("REL_$version", "Release $version")
+        git.branch("RB_$version")
+    }
+}
 
-task release(dependsOn: [uploadDists, ':docs:uploadDocs']) << {
-    // Update the version properties in gradle.properties
-//    Properties props = new Properties()
-//    File propsFile = file('gradle.properties')
-//    propsFile.withReader { props.load(it) }
-//    props.previousVersion = props.nextVersion
-//    def nextVersion = props.nextVersion.split('\\.')
-//    nextVersion[1] = (nextVersion[1] as Integer) +1
-//    props.nextVersion = nextVersion.join('.')
-//    propsFile.withWriter { props.store(it, 'properties') }
-// todo rewrite release functionality for Git. Until then we have to do tagging and branching manually.
+task releaseArtifacts {
+    description = 'Builds the release artifacts'
+    group = 'release'
+    dependsOn releaseVersion, tag, assemble, ':docs:websiteDocs'
+}
+
+task release {
+    description = 'Builds, tests and uploads the release artifacts'
+    group = 'release'
+    dependsOn releaseVersion, releaseArtifacts, testedDists, uploadDists, ':docs:uploadDocs'
 }
 
 task wrapper(type: Wrapper, dependsOn: binZip)
@@ -378,8 +453,6 @@ wrapper.doFirst {task ->
     }
 }
 
-task rebuildWrapper(dependsOn: [clean, wrapper])
-
 def groovyProjects() {
     subprojects.findAll { project -> project.name != 'docs' }
 }
@@ -393,98 +466,3 @@ def pluginProjects() {
         project(it)
     }
 }
-
-class Version {
-    String versionNumber
-    Date buildTime
-    Boolean release = null
-
-    def Version(project) {
-        this.versionNumber = project.nextVersion
-        File timestampFile = new File(project.buildDir, 'timestamp.txt')
-        if (!timestampFile.isFile()) {
-            timestampFile.parentFile.mkdirs()
-            timestampFile.createNewFile()
-        } else {
-            boolean uptodate = true
-            def modified = timestampFile.lastModified()
-            project.project(':core').fileTree('src/main').visit {fte ->
-                if (fte.file.isFile() && fte.lastModified > modified) {
-                    uptodate = false
-                    fte.stopVisiting()
-                }
-            }
-            if (!uptodate) {
-                timestampFile.setLastModified(new Date().time)
-            }
-        }
-        buildTime = new Date(timestampFile.lastModified())
-
-        project.gradle.taskGraph.whenReady {graph ->
-            if (!graph.hasTask(':release')) {
-                this.versionNumber += "-" + getTimestamp()
-                release = false
-            } else {
-                release = true
-            }
-        }
-    }
-
-    String toString() {
-        versionNumber
-    }
-
-    String getTimestamp() {
-        new SimpleDateFormat('yyyyMMddHHmmssZ').format(buildTime)
-    }
-
-    boolean isRelease() {
-        if (release == null) {
-            throw new GradleException("Can't determine whether this is a release build before the task graph is populated")
-        }
-        return release
-    }
-
-    String getDistributionUrl() {
-        if (release) {
-            'https://dav.codehaus.org/dist/gradle'
-        } else {
-            'https://dav.codehaus.org/snapshots.dist/gradle'
-        }
-    }
-}
-
-class Install extends Sync {
-
-    String installDirProperyName
-    File installDir
-
-    def Install() {
-        addPropertyCheck()
-        doLast {
-            ant.chmod(file: "$installDir/bin/gradle", perm: 'ugo+x')
-        }
-    }
-
-    private void addPropertyCheck() {
-        project.gradle.taskGraph.whenReady {graph ->
-            if (graph.hasTask(path)) {
-                // Do this early to ensure that the properties we need have been set, and fail early
-                if (!project.hasProperty(installDirProperyName)) {
-                    throw new RuntimeException("You can't install without setting the $installDirProperyName property.")
-                }
-                installDir = project.file(this.project."$installDirProperyName")
-                if (installDir.file) {
-                    throw new RuntimeException("Install directory $installDir does not look like a Gradle installation. Cannot delete it to install.")
-                }
-                if (installDir.directory) {
-                    File libDir = new File(installDir, "lib")
-                    if (!libDir.directory || !libDir.list().findAll { it.matches('gradle.*\\.jar')}) {
-                        throw new RuntimeException("Install directory $installDir does not look like a Gradle installation. Cannot delete it to install.")
-                    }
-                }
-                into installDir
-            }
-        }
-    }
-}
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index afc59b1..b8ada1f 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -22,11 +22,9 @@ repositories {
 }
 
 dependencies {
-    compile 'com.svnkit:svnkit:1.1.6', 'com.svnkit:svnkit-javahl:1.1.6'
     compile gradleApi()
     // todo Actually it should be only groovy, but without junit we get a strange error. We need to understand this.
     groovy localGroovy()
-    testCompile "junit:junit:4.7"
 }
 
 checkstyleConfigDir = "$rootDir/../config/checkstyle"
diff --git a/buildSrc/src/main/groovy/org/gradle/build/Git.groovy b/buildSrc/src/main/groovy/org/gradle/build/Git.groovy
new file mode 100644
index 0000000..d9c9fe5
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/Git.groovy
@@ -0,0 +1,40 @@
+package org.gradle.build
+
+import org.gradle.api.Project
+
+class Git {
+    private final Project project
+
+    def Git(Project project) {
+        this.project = project
+    }
+
+    def checkNoModifications() {
+        println 'checking for modifications'
+        def stdout = new ByteArrayOutputStream()
+        project.exec {
+            executable = 'git'
+            args = ['status', '--porcelain']
+            standardOutput = stdout
+        }
+        if (stdout.toByteArray().length > 0) {
+            throw new RuntimeException('Uncommited changes found in the source tree:\n' + stdout.toString())
+        }
+    }
+
+    def tag(String tag, String message) {
+        println "tagging with $tag"
+        project.exec {
+            executable = 'git'
+            args = ['tag', '-a', tag, '-m', message]
+        }
+    }
+
+    def branch(String branch) {
+        println "creating branch $branch"
+        project.exec {
+            executable = 'git'
+            args = ['branch', branch]
+        }
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/org/gradle/build/Install.groovy b/buildSrc/src/main/groovy/org/gradle/build/Install.groovy
new file mode 100644
index 0000000..f1ab84a
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/Install.groovy
@@ -0,0 +1,38 @@
+package org.gradle.build
+
+import org.gradle.api.tasks.Sync
+
+class Install extends Sync {
+
+    String installDirProperyName
+    File installDir
+
+    def Install() {
+        addPropertyCheck()
+        doLast {
+            ant.chmod(file: "$installDir/bin/gradle", perm: 'ugo+x')
+        }
+    }
+
+    private void addPropertyCheck() {
+        project.gradle.taskGraph.whenReady {graph ->
+            if (graph.hasTask(path)) {
+                // Do this early to ensure that the properties we need have been set, and fail early
+                if (!project.hasProperty(installDirProperyName)) {
+                    throw new RuntimeException("You can't install without setting the $installDirProperyName property.")
+                }
+                installDir = project.file(this.project."$installDirProperyName")
+                if (installDir.file) {
+                    throw new RuntimeException("Install directory $installDir does not look like a Gradle installation. Cannot delete it to install.")
+                }
+                if (installDir.directory) {
+                    File libDir = new File(installDir, "lib")
+                    if (!libDir.directory || !libDir.list().findAll { it.matches('gradle.*\\.jar')}) {
+                        throw new RuntimeException("Install directory $installDir does not look like a Gradle installation. Cannot delete it to install.")
+                    }
+                }
+                into installDir
+            }
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/Version.groovy b/buildSrc/src/main/groovy/org/gradle/build/Version.groovy
new file mode 100644
index 0000000..9fa1938
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/Version.groovy
@@ -0,0 +1,64 @@
+package org.gradle.build
+
+import java.text.SimpleDateFormat
+import org.gradle.api.GradleException
+
+class Version {
+    String versionNumber
+    Date buildTime
+    Boolean release = null
+
+    def Version(project) {
+        this.versionNumber = project.nextVersion
+        File timestampFile = new File(project.buildDir, 'timestamp.txt')
+        if (!timestampFile.isFile()) {
+            timestampFile.parentFile.mkdirs()
+            timestampFile.createNewFile()
+        } else {
+            boolean uptodate = true
+            def modified = timestampFile.lastModified()
+            project.project(':core').fileTree('src/main').visit {fte ->
+                if (fte.file.isFile() && fte.lastModified > modified) {
+                    uptodate = false
+                    fte.stopVisiting()
+                }
+            }
+            if (!uptodate) {
+                timestampFile.setLastModified(new Date().time)
+            }
+        }
+        buildTime = new Date(timestampFile.lastModified())
+
+        project.gradle.taskGraph.whenReady {graph ->
+            if (!graph.hasTask(':releaseVersion')) {
+                this.versionNumber += "-" + getTimestamp()
+                release = false
+            } else {
+                release = true
+            }
+        }
+    }
+
+    String toString() {
+        versionNumber
+    }
+
+    String getTimestamp() {
+        new SimpleDateFormat('yyyyMMddHHmmssZ').format(buildTime)
+    }
+
+    boolean isRelease() {
+        if (release == null) {
+            throw new GradleException("Can't determine whether this is a release build before the task graph is populated")
+        }
+        return release
+    }
+
+    String getDistributionUrl() {
+        if (release) {
+            'https://dav.codehaus.org/dist/gradle'
+        } else {
+            'https://dav.codehaus.org/snapshots.dist/gradle'
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSampleDocsTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSampleDocsTask.groovy
index cfa7ecd..7fc7365 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSampleDocsTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/AssembleSampleDocsTask.groovy
@@ -11,6 +11,9 @@ import org.w3c.dom.Element
 import groovy.xml.dom.DOMCategory
 import org.gradle.api.tasks.OutputFile
 
+/**
+ * Generates a chapter containing a summary of the readme files for the samples.
+ */
 class AssembleSamplesDocTask extends SourceTask {
     @OutputFile
     File destFile
@@ -74,9 +77,7 @@ class AssembleSamplesDocTask extends SourceTask {
                                     filename(sample.dir)
                                 }
                             }
-                            td {
-                                appendChild sample.firstPara
-                            }
+                            td(sample.firstPara)
                         }
                     }
                 }
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/BuildableDOMCategory.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/BuildableDOMCategory.groovy
new file mode 100644
index 0000000..cc993ee
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/BuildableDOMCategory.groovy
@@ -0,0 +1,59 @@
+package org.gradle.build.docs
+
+import org.w3c.dom.Element
+
+class BuildableDOMCategory {
+    public static setText(Element element, String value) {
+        while (element.hasChildNodes()) {
+            element.removeChild(element.getFirstChild())
+        }
+        element.appendChild(element.ownerDocument.createTextNode(value))
+    }
+
+    public static setChildren(Element element, Closure cl) {
+        while (element.hasChildNodes()) {
+            element.removeChild(element.getFirstChild())
+        }
+        leftShift(element, cl)
+    }
+
+    public static leftShift(Element parent, Closure cl) {
+        DomBuilder builder = new DomBuilder(parent)
+        cl.delegate = builder
+        cl.call()
+    }
+
+    public static leftShift(Element parent, Element node) {
+        parent.appendChild(parent.ownerDocument.importNode(node, true))
+    }
+
+    public static addFirst(Element parent, Closure cl) {
+        DomBuilder builder = new DomBuilder(parent.ownerDocument, null)
+        cl.delegate = builder
+        cl.call()
+        def firstChild = parent.firstChild
+        builder.elements.each { element ->
+            parent.insertBefore(element, firstChild)
+        }
+    }
+
+    public static addBefore(Element sibling, Closure cl) {
+        DomBuilder builder = new DomBuilder(sibling.ownerDocument, null)
+        cl.delegate = builder
+        cl.call()
+        def parent = sibling.parentNode
+        builder.elements.each { element ->
+            parent.insertBefore(element, sibling)
+        }
+    }
+
+    public static addAfter(Element sibling, Closure cl) {
+        DomBuilder builder = new DomBuilder(sibling.ownerDocument, null)
+        cl.delegate = builder
+        cl.call()
+        def parent = sibling.parentNode
+        builder.elements.each { element ->
+            parent.insertBefore(element, sibling.nextSibling)
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/DomBuilder.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/DomBuilder.groovy
index f2af2da..a1608a8 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/DomBuilder.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/DomBuilder.groovy
@@ -6,15 +6,29 @@ import org.w3c.dom.Node
 
 class DomBuilder extends BuilderSupport {
     Document document
+    Node parent
+    List elements = []
 
-    def DomBuilder(document) {
-        this.document = document;
+    def DomBuilder(Document document) {
+        this.document = document
+        this.parent = document
+    }
+    
+    def DomBuilder(Element parent) {
+        this.document = parent.ownerDocument
+        this.parent = parent
+    }
+
+    def DomBuilder(Document document, Element parent) {
+        this.document = document
+        this.parent = parent
     }
 
     protected Element createNode(Object name) {
         Element element = document.createElement(name as String)
-        if (document.documentElement == null) {
-            document.appendChild(element)
+        if (getCurrent() == null) {
+            elements << element
+            parent?.appendChild(element)
         }
         return element
     }
@@ -29,7 +43,11 @@ class DomBuilder extends BuilderSupport {
 
     protected Element createNode(Object name, Map attributes, Object value) {
         Element element = createNode(name, attributes)
-        element.appendChild(document.createTextNode(value as String))
+        if (value instanceof Node) {
+            element.appendChild(document.importNode(value, true))       
+        } else {
+            element.appendChild(document.createTextNode(value as String))
+        }
         return element
     }
 
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
index 284f18b..b4a1977 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/UserGuideTransformTask.groovy
@@ -19,14 +19,10 @@ package org.gradle.build.docs
 
 import groovy.xml.MarkupBuilder
 import groovy.xml.dom.DOMCategory
-import groovy.xml.dom.DOMUtil
-import javax.xml.parsers.DocumentBuilder
-import javax.xml.parsers.DocumentBuilderFactory
 import org.gradle.api.DefaultTask
 import org.gradle.api.file.FileCollection
 import org.w3c.dom.Document
 import org.w3c.dom.Element
-import org.w3c.dom.Node
 import org.gradle.api.tasks.*
 
 /**
@@ -56,35 +52,13 @@ public class UserGuideTransformTask extends DefaultTask {
 
     @TaskAction
     def transform() {
-        Element root
-
-        destFile.parentFile.mkdirs()
-
-        System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration",
-                "org.apache.xerces.parsers.XIncludeParserConfiguration")
-
-        // Set the thread context classloader to pick up the correct XML parser
-        def uris = classpath.getFiles().collect {it.toURI().toURL()}
-        def classloader = new URLClassLoader(uris as URL[], getClass().classLoader)
-        def oldClassloader = Thread.currentThread().getContextClassLoader()
-        Thread.currentThread().setContextClassLoader(classloader)
-        try {
-
-            root = loadAndTransform()
-
-        } finally {
-            Thread.currentThread().setContextClassLoader(oldClassloader)
-        }
-
-        destFile.parentFile.mkdirs()
-        destFile.withOutputStream {OutputStream stream ->
-            DOMUtil.serialize(root, stream)
-        }
+        XIncludeAwareXmlProvider provider = new XIncludeAwareXmlProvider(classpath)
+        provider.parse(sourceFile)
+        transform(provider.document)
+        provider.write(destFile)
     }
 
-    private Element loadAndTransform() {
-        Document doc = parseSourceFile()
-        Element root = doc.documentElement
+    private def transform(Document doc) {
         use(DOMCategory) {
             addVersionInfo(doc)
             applyConditionalChunks(doc)
@@ -93,8 +67,6 @@ public class UserGuideTransformTask extends DefaultTask {
             transformWebsiteLinks(doc)
             fixProgramListings(doc)
         }
-
-        return root
     }
 
     def addVersionInfo(Document doc) {
@@ -122,6 +94,9 @@ public class UserGuideTransformTask extends DefaultTask {
             xml.links {
                 doc.documentElement.depthFirst().findAll { it.name() == 'apilink' }.each {Element element ->
                     String className = element.'@class'
+                    if (!className) {
+                        throw new RuntimeException('No "class" attribute specified for <apilink> element.')
+                    }
                     String methodName = element.'@method'
                     String lang = element.'@lang' ?: 'java'
 
@@ -223,7 +198,7 @@ public class UserGuideTransformTask extends DefaultTask {
                             exampleElement.appendChild(outputTitle)
 
                             Element screenElement = doc.createElement('screen')
-                            File srcFile = new File(sourceFile.parentFile, "../../../src/samples/userguideOutput/${outputFile}")
+                            File srcFile = new File(sourceFile.parentFile, "../../../src/samples/userguideOutput/${outputFile}").canonicalFile
                             screenElement.appendChild(doc.createTextNode("> gradle $args\n" + normalise(srcFile.text)))
                             exampleElement.appendChild(screenElement)
                         } else if (child.name() == 'test') {
@@ -280,11 +255,4 @@ public class UserGuideTransformTask extends DefaultTask {
             }
         }
     }
-
-    private Document parseSourceFile() {
-        DocumentBuilderFactory factory = Class.forName('com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl', true, Thread.currentThread().contextClassLoader).newInstance()
-        factory.setNamespaceAware(true)
-        DocumentBuilder builder = factory.newDocumentBuilder()
-        return builder.parse(sourceFile)
-    }
 }
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/XIncludeAwareXmlProvider.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/XIncludeAwareXmlProvider.groovy
new file mode 100644
index 0000000..cfe9c8e
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/XIncludeAwareXmlProvider.groovy
@@ -0,0 +1,51 @@
+package org.gradle.build.docs
+
+import org.w3c.dom.Document
+import javax.xml.parsers.DocumentBuilder
+import javax.xml.parsers.DocumentBuilderFactory
+import org.w3c.dom.Element
+import groovy.xml.dom.DOMUtil
+
+class XIncludeAwareXmlProvider {
+    final Iterable<java.io.File> classpath
+    Element root
+
+    XIncludeAwareXmlProvider(Iterable<File> classpath) {
+        this.classpath = classpath
+    }
+
+    Element parse(File sourceFile) {
+        System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration",
+                "org.apache.xerces.parsers.XIncludeParserConfiguration")
+
+        // Set the thread context classloader to pick up the correct XML parser
+        def uris = classpath.collect {it.toURI().toURL()}
+        def classloader = new URLClassLoader(uris as URL[], getClass().classLoader)
+        def oldClassloader = Thread.currentThread().getContextClassLoader()
+        Thread.currentThread().setContextClassLoader(classloader)
+        try {
+            root = parseSourceFile(sourceFile).documentElement
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldClassloader)
+        }
+        return root
+    }
+
+    void write(File destFile) {
+        destFile.withOutputStream {OutputStream stream ->
+            DOMUtil.serialize(root, stream)
+        }
+    }
+
+    Document getDocument() {
+        return root.ownerDocument
+    }
+    
+    private Document parseSourceFile(File sourceFile) {
+        DocumentBuilderFactory factory = Class.forName('com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl', true, Thread.currentThread().contextClassLoader).newInstance()
+        factory.setNamespaceAware(true)
+        DocumentBuilder builder = factory.newDocumentBuilder()
+        return builder.parse(sourceFile)
+    }
+
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/AssembleDslDocTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/AssembleDslDocTask.groovy
new file mode 100644
index 0000000..094ac82
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/AssembleDslDocTask.groovy
@@ -0,0 +1,119 @@
+package org.gradle.build.docs.dsl
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.InputFiles
+import org.w3c.dom.Document
+import groovy.xml.dom.DOMCategory
+import org.w3c.dom.Element
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.build.docs.XIncludeAwareXmlProvider
+import org.gradle.build.docs.BuildableDOMCategory
+
+/**
+ * Generates the docbook source for the DSL documentation. Uses meta-data extracted from the source, meta-data about the
+ * plugins, plus a docbook template file.
+ */
+class AssembleDslDocTask extends DefaultTask {
+    @InputFile
+    File sourceFile
+    @InputFile
+    File classMetaDataFile
+    @InputFile
+    File pluginsMetaDataFile
+    @InputDirectory
+    File classDocbookDir
+    @OutputFile
+    File destFile
+    @InputFiles
+    FileCollection classpath;
+
+    @TaskAction
+    def transform() {
+        XIncludeAwareXmlProvider provider = new XIncludeAwareXmlProvider(classpath)
+        provider.parse(sourceFile)
+        transformDocument(provider.document)
+        provider.write(destFile)
+    }
+
+    private def transformDocument(Document document) {
+        use(DOMCategory) {
+            use(BuildableDOMCategory) {
+                Map<String, ClassMetaData> classes = loadClassMetaData()
+                Map<String, ExtensionMetaData> extensions = loadPluginsMetaData()
+                DslModel model = new DslModel(classDocbookDir, document, classpath, classes, extensions)
+                def root = document.documentElement
+                root.table.each { Element table ->
+                    insertTypes(table, model)
+                }
+            }
+        }
+    }
+
+    def loadPluginsMetaData() {
+        XIncludeAwareXmlProvider provider = new XIncludeAwareXmlProvider(classpath)
+        provider.parse(pluginsMetaDataFile)
+        Map<String, ExtensionMetaData> extensions = [:]
+        provider.root.plugin.each { Element plugin ->
+            def description = plugin.'@description'
+            plugin.extends.each { Element e ->
+                def targetClass = e.'@targetClass'
+                def extensionClass = e.'@extensionClass'
+                def extension = extensions[targetClass]
+                if (!extension) {
+                    extension = new ExtensionMetaData(targetClass)
+                    extensions[targetClass] = extension
+                }
+                extension.add(description, extensionClass)
+            }
+        }
+        return extensions
+    }
+
+    def loadClassMetaData() {
+        Map<String, ClassMetaData> classes;
+        classMetaDataFile.withInputStream { InputStream instr ->
+            ObjectInputStream ois = new ObjectInputStream(instr) {
+                @Override protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) {
+                    return AssembleDslDocTask.classLoader.loadClass(objectStreamClass.name)
+                }
+
+            }
+            classes = ois.readObject()
+        }
+        return classes
+    }
+
+    def insertTypes(Element typeTable, DslModel model) {
+        typeTable.addFirst {
+            thead {
+                tr {
+                    td('Type')
+                    td('Description')
+                }
+            }
+        }
+
+        typeTable.tr.each { Element tr ->
+            insertType(tr, model)
+        }
+    }
+
+    def insertType(Element tr, DslModel model) {
+        String className = tr.td[0].text().trim()
+        ClassDoc classDoc = model.getClassDoc(className)
+        Element root = tr.ownerDocument.documentElement
+
+        root << classDoc.classSection
+
+        tr.children = {
+            td {
+                link(linkend: classDoc.id, classDoc.classSimpleName)
+            }
+            td(classDoc.description)
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ClassDoc.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ClassDoc.groovy
new file mode 100644
index 0000000..3565e3e
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ClassDoc.groovy
@@ -0,0 +1,111 @@
+package org.gradle.build.docs.dsl
+
+import org.w3c.dom.Element
+
+class ClassDoc {
+    final Element classSection
+    final String className
+    final String id
+    final String classSimpleName
+    final ClassMetaData classMetaData
+
+    ClassDoc(String className, Element classSection, ClassMetaData classMetaData, ExtensionMetaData extensionMetaData, DslModel model) {
+        this.classSection = classSection
+        this.className = className
+        id = "dsl:$className"
+        classSimpleName = className.tokenize('.').last()
+        this.classMetaData = classMetaData
+
+        classSection['@id'] = id
+        classSection.addFirst {
+            title(classSimpleName)
+        }
+
+        propertiesTable.tr.each { Element tr ->
+            def cells = tr.td
+            if (cells.size() != 2) {
+                throw new RuntimeException("Expected 2 cells in <tr>, found: $tr")
+            }
+            String propName = cells[0].text().trim()
+            PropertyMetaData property = classMetaData.classProperties[propName]
+            if (!property) {
+                throw new RuntimeException("No metadata for property '$className.$propName'. Available properties: ${classMetaData.classProperties.keySet()}")
+            }
+            String type = property.type
+            tr.td[0].children = { literal(propName) }
+            tr.td[0].addAfter {
+                td {
+                    if (type.startsWith('org.gradle')) {
+                        apilink('class': type)
+                    } else if (type.startsWith('java.lang.') || type.startsWith('java.util.') || type.startsWith('java.io.')) {
+                        classname(type.tokenize('.').last())
+                    } else {
+                        classname(type)
+                    }
+                    if (!property.writeable) {
+                        text(" (read-only)")
+                    }
+                }
+            }
+        }
+
+        if (classMetaData.superClassName) {
+            ClassDoc supertype = model.getClassDoc(classMetaData.superClassName)
+            supertype.propertiesTable.tr.each { Element tr ->
+                propertiesTable << tr
+            }
+            supertype.methodsTable.tr.each { Element tr ->
+                methodsTable << tr
+            }
+        }
+
+        classSection.section[0].addBefore {
+            section {
+                title('API Documentation')
+                para {
+                    apilink('class': className, lang: lang)
+                }
+            }
+        }
+
+        extensionMetaData.extensionClasses.each { Map map ->
+            ClassDoc extensionClassDoc = model.getClassDoc(map.extensionClass)
+            classSection << extensionClassDoc.classSection
+            
+            classSection.lastChild.title[0].text = "${map.plugin} - ${extensionClassDoc.classSimpleName}"
+        }
+    }
+
+    Element getPropertiesTable() {
+        return getSection('Properties').table[0]
+    }
+
+    Element getMethodsTable() {
+        return getSection('Methods').table[0]
+    }
+
+    String getLang() {
+        return classMetaData.groovy ? 'groovy' : 'java'
+    }
+
+    private Element getSection(String title) {
+        def sections = classSection.section.findAll { it.title[0].text().trim() == title }
+        if (sections.size() < 1) {
+            throw new RuntimeException("Docbook content for $className does not contain a '$title' section.")
+        }
+        return sections[0]
+    }
+
+    Element getHasDescription() {
+        def paras = classSection.para
+        return paras.size() > 0 ? paras[0] : null
+    }
+
+    Element getDescription() {
+        def paras = classSection.para
+        if (paras.size() < 1) {
+            throw new RuntimeException("Docbook content for $className does not contain a description paragraph.")
+        }
+        return paras[0]
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ClassMetaData.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ClassMetaData.java
new file mode 100644
index 0000000..4793210
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ClassMetaData.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.build.docs.dsl;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ClassMetaData implements Serializable {
+    private final String superClassName;
+    private final boolean groovy;
+    private final Map<String, PropertyMetaData> classProperties = new HashMap<String, PropertyMetaData>();
+
+    public ClassMetaData(String superClassName, boolean isGroovy) {
+        this.superClassName = superClassName;
+        groovy = isGroovy;
+    }
+
+    public Map<String, PropertyMetaData> getClassProperties() {
+        return classProperties;
+    }
+
+    public boolean isGroovy() {
+        return groovy;
+    }
+
+    public String getSuperClassName() {
+        return superClassName;
+    }
+
+    public void addReadableProperty(String name, String type) {
+        PropertyMetaData property = getProperty(name);
+        property.setType(type);
+    }
+
+    public void addWriteableProperty(String name, String type) {
+        PropertyMetaData property = getProperty(name);
+        property.setWriteable(true);
+    }
+
+    private PropertyMetaData getProperty(String name) {
+        PropertyMetaData property = classProperties.get(name);
+        if (property == null) {
+            property = new PropertyMetaData();
+            classProperties.put(name, property);
+        }
+        return property;
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/DslModel.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/DslModel.groovy
new file mode 100644
index 0000000..137edad
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/DslModel.groovy
@@ -0,0 +1,43 @@
+package org.gradle.build.docs.dsl
+
+import org.w3c.dom.Document
+import org.gradle.build.docs.XIncludeAwareXmlProvider
+
+class DslModel {
+    private final File classDocbookDir
+    private final Document document
+    private final Iterable<File> classpath
+    private final Map<String, ClassDoc> classes = [:]
+    private final Map<String, ClassMetaData> classMetaData
+    private final Map<String, ExtensionMetaData> extensionMetaData
+
+    DslModel(File classDocbookDir, Document document, Iterable<File> classpath, Map<String, ClassMetaData> classMetaData, Map<String, ExtensionMetaData> extensionMetaData) {
+        this.classDocbookDir = classDocbookDir
+        this.document = document
+        this.classpath = classpath
+        this.classMetaData = classMetaData
+        this.extensionMetaData = extensionMetaData
+    }
+
+    def getClassDoc(String className) {
+        ClassDoc classDoc = classes[className]
+        if (classDoc == null) {
+            ClassMetaData classMetaData = classMetaData[className]
+            if (!classMetaData) {
+                classMetaData = new ClassMetaData(null, false)
+            }
+            ExtensionMetaData extensionMetaData = extensionMetaData[className]
+            if (!extensionMetaData) {
+                extensionMetaData = new ExtensionMetaData(className)
+            }
+            File classFile = new File(classDocbookDir, "${className}.xml")
+            if (!classFile.isFile()) {
+                throw new RuntimeException("Docbook source file not found for class '$className' in $classDocbookDir.")
+            }
+            XIncludeAwareXmlProvider provider = new XIncludeAwareXmlProvider(classpath)
+            classDoc = new ClassDoc(className, provider.parse(classFile), classMetaData, extensionMetaData, this)
+            classes[className] = classDoc
+        }
+        return classDoc
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ExtensionMetaData.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ExtensionMetaData.groovy
new file mode 100644
index 0000000..0800088
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ExtensionMetaData.groovy
@@ -0,0 +1,14 @@
+package org.gradle.build.docs.dsl
+
+class ExtensionMetaData {
+    final String targetClass
+    final Set<Map<String, String>> extensionClasses = new HashSet()
+
+    ExtensionMetaData(String targetClass) {
+        this.targetClass = targetClass
+    }
+    
+    def void add(String plugin, String extensionClass) {
+        extensionClasses << [plugin: plugin, extensionClass: extensionClass]
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ExtractDslMetaDataTask.groovy b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ExtractDslMetaDataTask.groovy
new file mode 100644
index 0000000..0f28757
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/ExtractDslMetaDataTask.groovy
@@ -0,0 +1,69 @@
+package org.gradle.build.docs.dsl
+
+import org.codehaus.groovy.groovydoc.GroovyFieldDoc
+import org.codehaus.groovy.groovydoc.GroovyMethodDoc
+import org.codehaus.groovy.groovydoc.GroovyRootDoc
+import org.codehaus.groovy.tools.groovydoc.GroovyDocTool
+import org.codehaus.groovy.tools.groovydoc.SimpleGroovyClassDoc
+import org.gradle.api.file.FileVisitDetails
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.SourceTask
+import org.gradle.api.tasks.TaskAction
+
+/**
+ * Extracts meta-data from the Groovy and Java source files which make up the Gradle DSL. Persists the meta-data to a file
+ * for later use in generating the docbook source for the DSL.
+ */
+class ExtractDslMetaDataTask extends SourceTask {
+    @OutputFile
+    def File destFile
+
+    @TaskAction
+    def extract() {
+        project.delete(temporaryDir)
+        project.copy { from source; into temporaryDir }
+        GroovyDocTool groovyDoc = new GroovyDocTool([temporaryDir.absolutePath] as String[])
+        List<String> files = []
+        project.fileTree(temporaryDir).visit { FileVisitDetails fvd ->
+            if (!fvd.isDirectory()) {
+                files << fvd.path
+            }
+        }
+        groovyDoc.add(files)
+
+        Map<String, ClassMetaData> allClasses = [:]
+        GroovyRootDoc rootDoc = groovyDoc.rootDoc
+        rootDoc.classes().each { SimpleGroovyClassDoc doc ->
+            String className = doc.qualifiedTypeName()
+            String superClassName = doc.superclass()?.qualifiedTypeName()
+            ClassMetaData classMetaData = new ClassMetaData(superClassName, doc.isGroovy())
+            allClasses[className] = classMetaData
+
+            doc.methods().each { GroovyMethodDoc method ->
+                if (method.name().matches("get.+")) {
+                    String propName = method.name()[3].toLowerCase() + method.name().substring(4)
+                    classMetaData.addReadableProperty(propName, method.returnType().qualifiedTypeName())
+                } else if (method.name().matches("set.+")) {
+                    String propName = method.name()[3].toLowerCase() + method.name().substring(4)
+                    classMetaData.addWriteableProperty(propName, method.returnType().qualifiedTypeName())
+                }
+            }
+
+            // This bit of ugliness is to get Groovydoc to resolve the type names for properties by pretending that
+            // they are fields
+            doc.fields.clear()
+            doc.fields.addAll(doc.properties())
+            doc.methods.clear()
+            doc.resolve(rootDoc)
+
+            doc.properties().each { GroovyFieldDoc field ->
+                classMetaData.addReadableProperty(field.name(), field.type().qualifiedTypeName())
+                classMetaData.addWriteableProperty(field.name(), field.type().qualifiedTypeName())
+            }
+        }
+
+        destFile.withObjectOutputStream { ObjectOutputStream outstr ->
+            outstr.writeObject(allClasses)
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/PropertyMetaData.java b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/PropertyMetaData.java
new file mode 100644
index 0000000..a1681cf
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/gradle/build/docs/dsl/PropertyMetaData.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.build.docs.dsl;
+
+import java.io.Serializable;
+
+public class PropertyMetaData implements Serializable {
+    private String type;
+    private boolean writeable;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public boolean isWriteable() {
+        return writeable;
+    }
+
+    public void setWriteable(boolean writeable) {
+        this.writeable = writeable;
+    }
+}
diff --git a/buildSrc/src/main/groovy/org/gradle/build/samples/WrapperProjectCreator.groovy b/buildSrc/src/main/groovy/org/gradle/build/samples/WrapperProjectCreator.groovy
index fb88d73..da4c223 100644
--- a/buildSrc/src/main/groovy/org/gradle/build/samples/WrapperProjectCreator.groovy
+++ b/buildSrc/src/main/groovy/org/gradle/build/samples/WrapperProjectCreator.groovy
@@ -29,7 +29,7 @@ class WrapperProjectCreator {
 
     static void createProject(File baseDir, File downloadUrlRoot, String gradleVersion) {
         String gradleScript = """
-createTask('$WRAPPER_TASK_NAME', type: $Wrapper.name).configure {
+task $WRAPPER_TASK_NAME(type: $Wrapper.name) {
     gradleVersion = '$gradleVersion'
     urlRoot = '${downloadUrlRoot.toURI().toURL()}'
     zipBase = Wrapper.PathBase.PROJECT
@@ -40,7 +40,7 @@ createTask('$WRAPPER_TASK_NAME', type: $Wrapper.name).configure {
     distributionPath = 'dist'
 }
 
-createTask('$TEST_TASK_NAME') {
+task $TEST_TASK_NAME << {
     println '$TEST_TASK_OUTPUT'
 }
 """
diff --git a/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptHead.txt b/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptHead.txt
index 0a316bc..784ed8d 100644
--- a/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptHead.txt
+++ b/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptHead.txt
@@ -7,8 +7,8 @@
 ##############################################################################
 
 # Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
-# GRADLE_OPTS="$GRADLE_OPTS -Xmx512"
-# JAVA_OPTS="$JAVA_OPTS -Xmx512"
+# GRADLE_OPTS="$GRADLE_OPTS -Xmx512m"
+# JAVA_OPTS="$JAVA_OPTS -Xmx512m"
 
 GRADLE_APP_NAME=Gradle
 
diff --git a/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptTail.txt b/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptTail.txt
index 6e4ef0e..082ebbc 100644
--- a/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptTail.txt
+++ b/buildSrc/src/main/resources/org/gradle/build/startscripts/unixStartScriptTail.txt
@@ -97,6 +97,8 @@ if $cygwin ; then
 
 fi
 
+GRADLE_APP_BASE_NAME=`basename "$0"`
+
 STARTER_MAIN_CLASS=org.gradle.launcher.GradleMain
 
 # Start the Profiler or the JVM
@@ -105,6 +107,7 @@ if $useprofiler ; then
 else
     exec "$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \
         -classpath "$CLASSPATH" \
+        -Dorg.gradle.appname="$GRADLE_APP_BASE_NAME" \
         $STARTER_MAIN_CLASS \
         "$@"
 fi
diff --git a/buildSrc/src/main/resources/org/gradle/build/startscripts/windowsStartScriptHead.txt b/buildSrc/src/main/resources/org/gradle/build/startscripts/windowsStartScriptHead.txt
index d8bcd5c..578b8b0 100644
--- a/buildSrc/src/main/resources/org/gradle/build/startscripts/windowsStartScriptHead.txt
+++ b/buildSrc/src/main/resources/org/gradle/build/startscripts/windowsStartScriptHead.txt
@@ -13,8 +13,8 @@
 if "%OS%"=="Windows_NT" setlocal
 
 @rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
- at rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512
- at rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512
+ at rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512m
+ at rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m
 
 set DIRNAME=%~dp0
 if "%DIRNAME%" == "" set DIRNAME=.\
diff --git a/config/checkstyle/checkstyle-api.xml b/config/checkstyle/checkstyle-api.xml
new file mode 100644
index 0000000..e6d6ff6
--- /dev/null
+++ b/config/checkstyle/checkstyle-api.xml
@@ -0,0 +1,38 @@
+<!--
+  ~ Copyright 2010 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<!DOCTYPE module PUBLIC
+        "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
+        "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+<module name="Checker">
+    <module name="SuppressionFilter">
+        <property name="file" value="${checkstyleConfigDir}/suppressions.xml"/>
+    </module>
+    <module name="JavadocPackage"/>
+
+    <module name="TreeWalker">
+        <module name="JavadocStyle"/>
+
+        <module name="JavadocType">
+            <property name="scope" value="package"/>
+        </module>
+
+        <!-- TODO - switch this on -->
+        <!--<module name="JavadocMethod">-->
+            <!--<property name="scope" value="package"/>-->
+        <!--</module>-->
+
+    </module>
+</module>
\ No newline at end of file
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index a686796..ad6374d 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -30,14 +30,26 @@
             <property name="option" value="text"/>
             <property name="tokens" value="LITERAL_CATCH"/>
         </module>
+        <module name="AvoidNestedBlocks"/>
 
         <!-- Braces -->
         <module name="NeedBraces"/>
 
         <!-- Coding -->
+        <module name="CovariantEquals"/>
+        <module name="DefaultComesLast"/>
         <module name="EmptyStatement"/>
         <module name="EqualsHashCode"/>
         <module name="ExplicitInitialization"/>
+        <module name="MultipleVariableDeclarations"/>
+        <module name="NoClone"/>
+        <module name="NoFinalizer"/>
+        <module name="RedundantThrows">
+            <property name="allowUnchecked" value="true"/>
+        </module>
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+        <module name="StringLiteralEquality"/>
         <module name="UnnecessaryParentheses"/>
 
         <!-- Design -->
@@ -47,11 +59,6 @@
         <module name="RedundantImport"/>
         <module name="UnusedImports"/>
 
-        <!--<module name="JavadocMethod">-->
-        <!--<property name="scope" value="public"/>-->
-        <!--<property name="allowMissingReturnTag" value="true"/>-->
-        <!--</module>-->
-
         <!-- Naming -->
         <module name="ClassTypeParameterName"/>
         <module name="ConstantName"/>
@@ -66,8 +73,10 @@
         <module name="ParameterName"/>
         <module name="StaticVariableName"/>
         <module name="TypeName"/>
+
     </module>
     <module name="RegexpHeader">
         <property name="headerFile" value="${checkstyleConfigDir}/required-header.txt"/>
     </module>
+    <module name="FileTabCharacter"/>
 </module>
\ No newline at end of file
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
index 0e3707c..5ca7629 100644
--- a/config/checkstyle/suppressions.xml
+++ b/config/checkstyle/suppressions.xml
@@ -5,6 +5,13 @@
     "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
 
 <suppressions>
-    <suppress checks="JavadocMethod"
-              files=".*/org/gradle/api/internal/.*"/>
+    <!-- These packages are duplicated in core, don't require a package-info.java in each place -->
+    <suppress checks="JavadocPackage"
+              files=".*[/\\]subprojects[/\\]gradle-maven[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\]plugins[/\\][^/\\]+"/>
+    <suppress checks="JavadocPackage"
+              files=".*[/\\]subprojects[/\\]gradle-plugins[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\]plugins[/\\][^/\\]+"/>
+    <suppress checks="JavadocPackage"
+              files=".*[/\\]subprojects[/\\]gradle-plugins[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\]tasks[/\\][^/\\]+"/>
+    <suppress checks="JavadocPackage"
+              files=".*[/\\]subprojects[/\\]gradle-scala[/\\]src[/\\]main[/\\]groovy[/\\]org[/\\]gradle[/\\]api[/\\]tasks[/\\][^/\\]+"/>
 </suppressions>
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 9eaa01d..d43770a 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
 #properties
 #Mon Nov 16 07:56:40 EST 2009
-previousVersion=0.9-preview-3
-nextVersion=0.9-rc-1
+previousVersion=0.9-rc-1
+nextVersion=0.9-rc-2
diff --git a/gradle/integTest.gradle b/gradle/integTest.gradle
new file mode 100644
index 0000000..c49bc1f
--- /dev/null
+++ b/gradle/integTest.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'java'
+
+configurations {
+    integTestCompile {
+        extendsFrom testCompile
+    }
+    integTestRuntime {
+        extendsFrom integTestCompile, testRuntime
+    }
+}
+
+sourceSets {
+    integTest {
+        compileClasspath = sourceSets.main.classes + sourceSets.test.classes + configurations.integTestCompile
+        runtimeClasspath = classes + compileClasspath + configurations.integTestRuntime
+    }
+}
+
+ideaModule {
+    testSourceDirs += sourceSets.integTest.groovy.srcDirs
+    testSourceDirs += sourceSets.integTest.resources.srcDirs
+    scopes.TEST.plus.add(configurations.integTestCompile)
+    scopes.TEST.plus.add(configurations.integTestRuntime)
+}
+
+eclipseClasspath {
+    plusConfigurations.add(configurations.integTestCompile)
+    plusConfigurations.add(configurations.integTestRuntime)
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy
index 5d3c258..e254f77 100644
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncePlugin.groovy
@@ -19,6 +19,8 @@ package org.gradle.api.plugins.announce;
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.plugins.announce.internal.AnnouncerFactory
+import org.gradle.api.plugins.announce.internal.DefaultAnnouncerFactory
 
 /**
  * This plugin allows to send announce messages to twitter.
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java
index a78863c..24be828 100644
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Announcer.java
@@ -16,7 +16,9 @@
 
 package org.gradle.api.plugins.announce;
 
-
+/**
+ * An {@code Announcer} allows messages to be sent.
+ */
 public interface Announcer {
   void send(String title, String message); 
 }
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncerFactory.java b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncerFactory.java
deleted file mode 100644
index 2d0acc8..0000000
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/AnnouncerFactory.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.announce;
-
-/**
- * @author Hans Dockter
- */
-public interface AnnouncerFactory {
-    Announcer createAnnouncer(String type);
-}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/DefaultAnnouncerFactory.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/DefaultAnnouncerFactory.groovy
deleted file mode 100644
index 2501a89..0000000
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/DefaultAnnouncerFactory.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.announce
-
-/**
- * @author Hans Dockter
- */
-class DefaultAnnouncerFactory implements AnnouncerFactory {
-    AnnouncePluginConvention announcePluginConvention
-
-    def DefaultAnnouncerFactory(announcePluginConvention) {
-        this.announcePluginConvention = announcePluginConvention;
-    }
-
-    Announcer createAnnouncer(String type) {
-        if (type == "twitter") {
-            String username = announcePluginConvention.username
-            String password = announcePluginConvention.password
-            return new Twitter(username, password)
-        } else if (type == "notify-send") {
-            return new NotifySend()
-        } else if (type == "snarl") {
-            return new Snarl()
-        } else if (type == "growl") {
-            return new Growl()
-        }
-        new DoNothingAnnouncer()
-    }
-}
-
-class DoNothingAnnouncer implements Announcer {
-    void send(String title, String message) {
-        // do nothing
-    }
-}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Growl.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Growl.groovy
deleted file mode 100644
index 62c9c62..0000000
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Growl.groovy
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.announce
-
-import org.gradle.process.internal.DefaultExecAction
-import org.gradle.process.internal.ExecAction
-
-class Growl implements Announcer {
-    void send(String title, String message) {
-        ExecAction execAction = new DefaultExecAction()
-        execAction.executable('growlnotify')
-        execAction.args('-m', message, title)
-        execAction.execute()
-    }
-}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/NotifySend.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/NotifySend.groovy
deleted file mode 100644
index 10a9b57..0000000
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/NotifySend.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.plugins.announce;
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-/**
- * This class wraps the Ubuntu Notify Send functionality.
- *
- * @author Hackergarten
- */
-
-class NotifySend implements Announcer {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(NotifySend)
-
-    public void send(String title, String message) {
-        def cmd = [
-                'notify-send',
-                title,
-                message
-        ]
-        try {
-            cmd.execute()
-        } catch (java.io.IOException e) {
-            LOGGER.warn('''Could not find notify-send command,
-              The programm is aviable in the libnotify-bin.
-              On ubuntu simple install it with: \\n   
-              sudo apt-get install libnotify-bin''')
-
-        }
-    }
-}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Snarl.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Snarl.groovy
deleted file mode 100644
index 6ec042a..0000000
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Snarl.groovy
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.plugins.announce
-
-class Snarl implements Announcer {
-  private static final float SNP_VERSION = 1.1f
-  private static final String HEAD = "type=SNP#?version=" + SNP_VERSION
-
-  public void send(String title, String message) {
-    send("localhost", title, message)
-  }
-
-  public void send(Collection hosts, String title, String message) {
-    hosts.each { host ->
-      send(host, title, message)
-    }
-  }
-
-  public void send(String host, String title, String message) {
-    with(new Socket(InetAddress.getByName(host), 9887)) { sock ->
-      with(new PrintWriter(sock.getOutputStream(), true)) { out ->
-        out.println(formatMessage(title, message))
-      }
-    }
-  }
-
-  private String formatMessage(String title, String message) {
-    def properties = [
-            formatProperty("action", "notification"),
-            formatProperty("app", "Gradle Snarl Notifier"),
-            formatProperty("class", "alert"),
-            formatProperty("title", title),
-            formatProperty("text", message),
-            formatProperty("icon", null),
-            formatProperty("timeout", "10")]
-
-    HEAD + properties.join('') + "\r\n"
-  }
-
-  private String formatProperty(String name, String value) {
-    if (!value) {
-      return ""
-    }
-    else {
-      return "#?" + name + "=" + value
-    }
-  }
-
-  private with(closable, closure) {
-    try {
-      closure(closable)
-    } finally {
-      try {
-        closable.close()
-      } catch (Exception e) {
-
-      }
-    }
-  }
-}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Twitter.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Twitter.groovy
deleted file mode 100644
index 914a025..0000000
--- a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/Twitter.groovy
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.plugins.announce;
-
-
-
-import org.slf4j.LoggerFactory
-import sun.misc.BASE64Encoder
-import org.slf4j.Logger
-
-/**
- * This class allows to send announce messages to twitter.
- *
- * @author hackergarten
- */
-class Twitter implements Announcer {
-
-  private static final String TWITTER_UPDATE_URL = "https://twitter.com/statuses/update.xml"
-
-  def username
-  def password
-
-  private static Logger logger = LoggerFactory.getLogger(Twitter)
-
-  Twitter(String username, String password) {
-    this.username = username
-    this.password = password
-  }
-
-  public void send(String title, String message) {
-    OutputStreamWriter out
-    URLConnection connection
-    try {
-      connection = new URL(TWITTER_UPDATE_URL).openConnection()
-      connection.doInput = true
-      connection.doOutput = true
-      connection.useCaches = false
-      String encoded = new BASE64Encoder().encodeBuffer("$username:$password".toString().bytes).trim()
-      connection.setRequestProperty "Authorization", "Basic " + encoded
-      out = new OutputStreamWriter(connection.outputStream)
-      out.write "status=" + URLEncoder.encode(message, "UTF-8")
-      out.close()
-       def result = ''
-       connection.inputStream.eachLine { result += it }
-      logger.info result
-      logger.info("Successfully send message: [$message] to twitter [$username]")
-    } catch (Exception e) {
-      logger.warn('Could not send message to twitter', e)
-    } finally {
-      connection?.disconnect()
-
-    }
-  
-  }
-}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.java b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.java
new file mode 100644
index 0000000..4c76454
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/AnnouncerFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce.internal;
+
+import org.gradle.api.plugins.announce.Announcer;
+
+/**
+ * @author Hans Dockter
+ */
+public interface AnnouncerFactory {
+    Announcer createAnnouncer(String type);
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy
new file mode 100644
index 0000000..d8e75cc
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactory.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce.internal
+
+import org.gradle.api.plugins.announce.AnnouncePluginConvention
+import org.gradle.api.plugins.announce.Announcer
+
+/**
+ * @author Hans Dockter
+ */
+class DefaultAnnouncerFactory implements AnnouncerFactory {
+    AnnouncePluginConvention announcePluginConvention
+
+    def DefaultAnnouncerFactory(announcePluginConvention) {
+        this.announcePluginConvention = announcePluginConvention;
+    }
+
+    Announcer createAnnouncer(String type) {
+        if (type == "twitter") {
+            String username = announcePluginConvention.username
+            String password = announcePluginConvention.password
+            return new Twitter(username, password)
+        } else if (type == "notify-send") {
+            return new NotifySend()
+        } else if (type == "snarl") {
+            return new Snarl()
+        } else if (type == "growl") {
+            return new Growl()
+        }
+        new DoNothingAnnouncer()
+    }
+}
+
+class DoNothingAnnouncer implements Announcer {
+    void send(String title, String message) {
+        // do nothing
+    }
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy
new file mode 100644
index 0000000..9021d66
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Growl.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce.internal
+
+import org.gradle.process.internal.DefaultExecAction
+import org.gradle.process.internal.ExecAction
+import org.gradle.api.plugins.announce.Announcer
+
+class Growl implements Announcer {
+    void send(String title, String message) {
+        ExecAction execAction = new DefaultExecAction()
+        execAction.executable('growlnotify')
+        execAction.args('-m', message, title)
+        execAction.execute()
+    }
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy
new file mode 100644
index 0000000..f683a27
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/NotifySend.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.announce.internal;
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.gradle.api.plugins.announce.Announcer
+
+/**
+ * This class wraps the Ubuntu Notify Send functionality.
+ *
+ * @author Hackergarten
+ */
+
+class NotifySend implements Announcer {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NotifySend)
+
+    public void send(String title, String message) {
+        def cmd = [
+                'notify-send',
+                title,
+                message
+        ]
+        try {
+            cmd.execute()
+        } catch (java.io.IOException e) {
+            LOGGER.warn('''Could not find notify-send command,
+              The programm is aviable in the libnotify-bin.
+              On ubuntu simple install it with: \\n   
+              sudo apt-get install libnotify-bin''')
+
+        }
+    }
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy
new file mode 100644
index 0000000..e5d9224
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Snarl.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.announce.internal
+
+import org.gradle.api.plugins.announce.Announcer
+
+class Snarl implements Announcer {
+  private static final float SNP_VERSION = 1.1f
+  private static final String HEAD = "type=SNP#?version=" + SNP_VERSION
+
+  public void send(String title, String message) {
+    send("localhost", title, message)
+  }
+
+  public void send(Collection hosts, String title, String message) {
+    hosts.each { host ->
+      send(host, title, message)
+    }
+  }
+
+  public void send(String host, String title, String message) {
+    with(new Socket(InetAddress.getByName(host), 9887)) { sock ->
+      with(new PrintWriter(sock.getOutputStream(), true)) { out ->
+        out.println(formatMessage(title, message))
+      }
+    }
+  }
+
+  private String formatMessage(String title, String message) {
+    def properties = [
+            formatProperty("action", "notification"),
+            formatProperty("app", "Gradle Snarl Notifier"),
+            formatProperty("class", "alert"),
+            formatProperty("title", title),
+            formatProperty("text", message),
+            formatProperty("icon", null),
+            formatProperty("timeout", "10")]
+
+    HEAD + properties.join('') + "\r\n"
+  }
+
+  private String formatProperty(String name, String value) {
+    if (!value) {
+      return ""
+    }
+    else {
+      return "#?" + name + "=" + value
+    }
+  }
+
+  private with(closable, closure) {
+    try {
+      closure(closable)
+    } finally {
+      try {
+        closable.close()
+      } catch (Exception e) {
+
+      }
+    }
+  }
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy
new file mode 100644
index 0000000..fa2cbbd
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/internal/Twitter.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.plugins.announce.internal;
+
+
+
+import org.slf4j.LoggerFactory
+import sun.misc.BASE64Encoder
+import org.slf4j.Logger
+import org.gradle.api.plugins.announce.Announcer
+
+/**
+ * This class allows to send announce messages to twitter.
+ *
+ * @author hackergarten
+ */
+class Twitter implements Announcer {
+
+  private static final String TWITTER_UPDATE_URL = "https://twitter.com/statuses/update.xml"
+
+  def username
+  def password
+
+  private static Logger logger = LoggerFactory.getLogger(Twitter)
+
+  Twitter(String username, String password) {
+    this.username = username
+    this.password = password
+  }
+
+  public void send(String title, String message) {
+    OutputStreamWriter out
+    URLConnection connection
+    try {
+      connection = new URL(TWITTER_UPDATE_URL).openConnection()
+      connection.doInput = true
+      connection.doOutput = true
+      connection.useCaches = false
+      String encoded = new BASE64Encoder().encodeBuffer("$username:$password".toString().bytes).trim()
+      connection.setRequestProperty "Authorization", "Basic " + encoded
+      out = new OutputStreamWriter(connection.outputStream)
+      out.write "status=" + URLEncoder.encode(message, "UTF-8")
+      out.close()
+       def result = ''
+       connection.inputStream.eachLine { result += it }
+      logger.info result
+      logger.info("Successfully send message: [$message] to twitter [$username]")
+    } catch (Exception e) {
+      logger.warn('Could not send message to twitter', e)
+    } finally {
+      connection?.disconnect()
+
+    }
+  
+  }
+}
diff --git a/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java
new file mode 100644
index 0000000..6ac0034
--- /dev/null
+++ b/subprojects/gradle-announce/src/main/groovy/org/gradle/api/plugins/announce/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A {link org.gradle.api.Plugin} for generating announcements from your build. 
+ */
+package org.gradle.api.plugins.announce;
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginConventionTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginConventionTest.groovy
index 6d223d6..f8ab6d6 100644
--- a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginConventionTest.groovy
+++ b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/AnnouncePluginConventionTest.groovy
@@ -18,6 +18,7 @@ package org.gradle.api.plugins.announce
 import org.gradle.api.Project
 import org.gradle.util.HelperUtil
 import spock.lang.Specification
+import org.gradle.api.plugins.announce.internal.AnnouncerFactory
 
 /**
  * @author Hans Dockter
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/DefaultAnnouncerFactoryTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/DefaultAnnouncerFactoryTest.groovy
deleted file mode 100644
index 266754e..0000000
--- a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/DefaultAnnouncerFactoryTest.groovy
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.announce
-
-import org.gradle.api.Project
-import org.gradle.util.HelperUtil
-import spock.lang.Specification
-
-/**
- * @author Hans Dockter
- */
-
-class DefaultAnnouncerFactoryTest extends Specification {
-    AnnouncePluginConvention announcePluginConvention = new AnnouncePluginConvention(project)
-    DefaultAnnouncerFactory announcerFactory = new DefaultAnnouncerFactory(announcePluginConvention)
-    Project project = HelperUtil.createRootProject()
-
-    def createForTwitter() {
-        announcePluginConvention.username = 'username'
-        announcePluginConvention.password = 'password'
-        
-        when:
-        Twitter twitter = announcerFactory.createAnnouncer('twitter')
-
-        then:
-        twitter.username == announcePluginConvention.username
-        twitter.password == announcePluginConvention.password
-    }
-
-    def createForSnarl() {
-        expect:
-        announcerFactory.createAnnouncer('snarl') instanceof Snarl
-    }
-
-    def createForNotifySend() {
-        expect:
-        announcerFactory.createAnnouncer('notify-send') instanceof NotifySend
-    }
-
-    def createForGrowl() {
-        expect:
-        announcerFactory.createAnnouncer('growl') instanceof Growl
-    }
-
-    def createWithUnknownType() {
-        expect:
-        announcerFactory.createAnnouncer('unknown') instanceof DoNothingAnnouncer
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/NotifySendTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/NotifySendTest.groovy
deleted file mode 100644
index 61f804f..0000000
--- a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/NotifySendTest.groovy
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.announce
-
-import org.junit.Ignore
-
-class NotifySendTest extends GroovyTestCase {
-
-  public void testWithException() {
-    use(ExceptionCategory) {
-      def notifier = new NotifySend()
-      notifier.send("title", "body")
-    }
-  }
-
- /* @Ignore
-  public void testCanSendMessage() {
-
-    use(MockCategory) {
-      def notifier = new NotifySend()
-      notifier.send("title", "body")
-      assert ['notify-send', 'title', 'body'] == MockCategory.capture, "nothing was executed"
-    }
-  }*/
-
-  public void testIntegrationTest() {
-    def notifier = new NotifySend()
-    notifier.send("title", "body")
-  }
-}
-
-
-private static class ExceptionCategory {
-  void execute(List list) {
-    throw new IOException()
-  }
-}
-
-private static class MockCategory {
-  def static capture
-
-  void execute(List list) {
-    capture = list
-  }
-}
-
-
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/SnarlTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/SnarlTest.groovy
deleted file mode 100644
index 4c49026..0000000
--- a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/SnarlTest.groovy
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.announce;
-
-
-class SnarlTest extends GroovyTestCase {
-//	public void testSend() {
-//		new Snarl().send("JUnit Test", "Hello from Groovy!");
-//	}
-//	public void testSend_MultipleHosts() {
-//		new Snarl().send(["localhost", "localhost", "localhost"], "JUnit Test", "Hello from Groovy!");
-//	}
-
-	void testMockSend() {
-//		use(PrintWriterCapture) {
-//			new Snarl().send("some title", "some message")
-//			assert PrintWriterCapture.capture == "type=SNP#?version=1.1#?action=notification#?app=Gradle Snarl Notifier#?class=alert#?title=some title#?text=some message#?timeout=10\r\n"
-//		}
-	}
-	
-}
-class PrintWriterCapture {
-  static capture
-
-  static void println(PrintWriter p, String input) {
-    capture = input
-  }
-}
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy
new file mode 100644
index 0000000..9c8898f
--- /dev/null
+++ b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/DefaultAnnouncerFactoryTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce.internal
+
+import org.gradle.api.Project
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+import org.gradle.api.plugins.announce.AnnouncePluginConvention
+
+/**
+ * @author Hans Dockter
+ */
+
+class DefaultAnnouncerFactoryTest extends Specification {
+    AnnouncePluginConvention announcePluginConvention = new AnnouncePluginConvention(project)
+    DefaultAnnouncerFactory announcerFactory = new DefaultAnnouncerFactory(announcePluginConvention)
+    Project project = HelperUtil.createRootProject()
+
+    def createForTwitter() {
+        announcePluginConvention.username = 'username'
+        announcePluginConvention.password = 'password'
+        
+        when:
+        Twitter twitter = announcerFactory.createAnnouncer('twitter')
+
+        then:
+        twitter.username == announcePluginConvention.username
+        twitter.password == announcePluginConvention.password
+    }
+
+    def createForSnarl() {
+        expect:
+        announcerFactory.createAnnouncer('snarl') instanceof Snarl
+    }
+
+    def createForNotifySend() {
+        expect:
+        announcerFactory.createAnnouncer('notify-send') instanceof NotifySend
+    }
+
+    def createForGrowl() {
+        expect:
+        announcerFactory.createAnnouncer('growl') instanceof Growl
+    }
+
+    def createWithUnknownType() {
+        expect:
+        announcerFactory.createAnnouncer('unknown') instanceof DoNothingAnnouncer
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy
new file mode 100644
index 0000000..51563f6
--- /dev/null
+++ b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/NotifySendTest.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce.internal
+
+class NotifySendTest extends GroovyTestCase {
+
+  public void testWithException() {
+    use(ExceptionCategory) {
+      def notifier = new NotifySend()
+      notifier.send("title", "body")
+    }
+  }
+
+ /* @Ignore
+  public void testCanSendMessage() {
+
+    use(MockCategory) {
+      def notifier = new NotifySend()
+      notifier.send("title", "body")
+      assert ['notify-send', 'title', 'body'] == MockCategory.capture, "nothing was executed"
+    }
+  }*/
+
+  public void testIntegrationTest() {
+    def notifier = new NotifySend()
+    notifier.send("title", "body")
+  }
+}
+
+
+private class ExceptionCategory {
+  void execute(List list) {
+    throw new IOException()
+  }
+}
+
+private class MockCategory {
+  def static capture
+
+  void execute(List list) {
+    capture = list
+  }
+}
+
+
diff --git a/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy
new file mode 100644
index 0000000..addd85e
--- /dev/null
+++ b/subprojects/gradle-announce/src/test/groovy/org/gradle/api/plugins/announce/internal/SnarlTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.announce.internal;
+
+
+class SnarlTest extends GroovyTestCase {
+//	public void testSend() {
+//		new Snarl().send("JUnit Test", "Hello from Groovy!");
+//	}
+//	public void testSend_MultipleHosts() {
+//		new Snarl().send(["localhost", "localhost", "localhost"], "JUnit Test", "Hello from Groovy!");
+//	}
+
+	void testMockSend() {
+//		use(PrintWriterCapture) {
+//			new Snarl().send("some title", "some message")
+//			assert PrintWriterCapture.capture == "type=SNP#?version=1.1#?action=notification#?app=Gradle Snarl Notifier#?class=alert#?title=some title#?text=some message#?timeout=10\r\n"
+//		}
+	}
+	
+}
+class PrintWriterCapture {
+  static capture
+
+  static void println(PrintWriter p, String input) {
+    capture = input
+  }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java
index 82f0bac..a9d3bd9 100644
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrPlugin.java
@@ -28,94 +28,78 @@ import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.DefaultSourceSet;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.JavaPlugin;
+
 import static org.gradle.api.plugins.JavaPlugin.COMPILE_CONFIGURATION_NAME;
+
 import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.plugins.antlr.internal.AntlrSourceVirtualDirectoryImpl;
 import org.gradle.api.tasks.ConventionValue;
 import org.gradle.api.tasks.SourceSet;
 
-
 /**
- * A plugin for adding Antlr support to {@link JavaPlugin java projects}
+ * A plugin for adding Antlr support to {@link JavaPlugin java projects}.
  *
  * @author Steve Ebersole
  */
 public class AntlrPlugin implements Plugin<Project> {
-	public static final String ANTLR_CONFIGURATION_NAME = "antlr";
+    public static final String ANTLR_CONFIGURATION_NAME = "antlr";
 
     public void apply(final Project project) {
-        project.getPlugins().apply( JavaPlugin.class );
+        project.getPlugins().apply(JavaPlugin.class);
 
-		// set up a configuration named 'antlr' for the user to specify the antlr libs to use in case
-		// they want a specific version etc.
-        Configuration antlrConfiguration = project.getConfigurations().add( ANTLR_CONFIGURATION_NAME )
-				.setVisible( false )
-				.setTransitive( false )
-				.setDescription( "The Antlr libraries to be used for this project." );
-        project.getConfigurations().getByName( COMPILE_CONFIGURATION_NAME ).extendsFrom( antlrConfiguration );
+        // set up a configuration named 'antlr' for the user to specify the antlr libs to use in case
+        // they want a specific version etc.
+        Configuration antlrConfiguration = project.getConfigurations().add(ANTLR_CONFIGURATION_NAME).setVisible(false)
+                .setTransitive(false).setDescription("The Antlr libraries to be used for this project.");
+        project.getConfigurations().getByName(COMPILE_CONFIGURATION_NAME).extendsFrom(antlrConfiguration);
 
         final ProjectInternal projectInternal = (ProjectInternal) project;
-		project.getConvention().getPlugin( JavaPluginConvention.class ).getSourceSets().allObjects(
-				new Action<SourceSet>() {
-					public void execute(SourceSet sourceSet) {
-						// for each source set we will:
-						// 		1) Add a new 'antlr' virtual directory mapping
-						final AntlrSourceVirtualDirectoryImpl antlrDirectoryDelegate = new AntlrSourceVirtualDirectoryImpl(
-								( (DefaultSourceSet) sourceSet ).getDisplayName(),
-								projectInternal.getFileResolver()
-						);
-						( (DynamicObjectAware) sourceSet ).getConvention().getPlugins().put(
-								AntlrSourceVirtualDirectory.NAME,
-								antlrDirectoryDelegate
-						);
-						final String srcDir = String.format( "src/%s/antlr", sourceSet.getName() );
-						antlrDirectoryDelegate.getAntlr().srcDir( srcDir );
-						sourceSet.getAllSource().add( antlrDirectoryDelegate.getAntlr() );
+        project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().allObjects(
+                new Action<SourceSet>() {
+                    public void execute(SourceSet sourceSet) {
+                        // for each source set we will:
+                        // 1) Add a new 'antlr' virtual directory mapping
+                        final AntlrSourceVirtualDirectoryImpl antlrDirectoryDelegate
+                                = new AntlrSourceVirtualDirectoryImpl(((DefaultSourceSet) sourceSet).getDisplayName(),
+                                projectInternal.getFileResolver());
+                        ((DynamicObjectAware) sourceSet).getConvention().getPlugins().put(
+                                AntlrSourceVirtualDirectory.NAME, antlrDirectoryDelegate);
+                        final String srcDir = String.format("src/%s/antlr", sourceSet.getName());
+                        antlrDirectoryDelegate.getAntlr().srcDir(srcDir);
+                        sourceSet.getAllSource().add(antlrDirectoryDelegate.getAntlr());
 
-						//		2) create an AntlrTask for this sourceSet following the gradle
-						//			naming conventions via call to sourceSet.getTaskName()
-						final String taskName = sourceSet.getTaskName("generate", "GrammarSource");
-						AntlrTask antlrTask = project.getTasks().add( taskName, AntlrTask.class );
-						antlrTask.setDescription(
-								String.format( "Processes the %s Antlr grammars.", sourceSet.getName() )
-						);
+                        // 2) create an AntlrTask for this sourceSet following the gradle
+                        //    naming conventions via call to sourceSet.getTaskName()
+                        final String taskName = sourceSet.getTaskName("generate", "GrammarSource");
+                        AntlrTask antlrTask = project.getTasks().add(taskName, AntlrTask.class);
+                        antlrTask.setDescription(String.format("Processes the %s Antlr grammars.",
+                                sourceSet.getName()));
 
-						//		3) set up convention mapping for default sources (allows user to not have to specify)
-						antlrTask.conventionMapping(
-								"defaultSource",
-								new ConventionValue() {
-									public Object getValue(Convention convention, IConventionAware conventionAwareObject) {
-										return antlrDirectoryDelegate.getAntlr();
-									}
-								}
-						);
+                        // 3) set up convention mapping for default sources (allows user to not have to specify)
+                        antlrTask.conventionMapping("defaultSource", new ConventionValue() {
+                            public Object getValue(Convention convention, IConventionAware conventionAwareObject) {
+                                return antlrDirectoryDelegate.getAntlr();
+                            }
+                        });
 
-						//		4) set up convention mapping for handling the 'antlr' dependency configuration
-						antlrTask.getConventionMapping().map(
-								"antlrClasspath",
-								new ConventionValue() {
-									public Object getValue(Convention convention, IConventionAware conventionAwareObject) {
-										return project.getConfigurations()
-												.getByName( ANTLR_CONFIGURATION_NAME )
-												.copy()
-												.setTransitive( true );
-                    				}
-                				}
-						);
+                        // 4) set up convention mapping for handling the 'antlr' dependency configuration
+                        antlrTask.getConventionMapping().map("antlrClasspath", new ConventionValue() {
+                            public Object getValue(Convention convention, IConventionAware conventionAwareObject) {
+                                return project.getConfigurations().getByName(ANTLR_CONFIGURATION_NAME).copy()
+                                        .setTransitive(true);
+                            }
+                        });
 
-						//		5) Set up the Antlr output directory (adding to javac inputs!)
-						final String outputDirectoryName = String.format(
-								"%s/generated-src/antlr/%s",
-								project.getBuildDir(),
-								sourceSet.getName()
-						);
-						final File outputDirectory = new File( outputDirectoryName );
-						antlrTask.setOutputDirectory( outputDirectory );
-						sourceSet.getJava().srcDir( outputDirectory );
+                        // 5) Set up the Antlr output directory (adding to javac inputs!)
+                        final String outputDirectoryName = String.format("%s/generated-src/antlr/%s",
+                                project.getBuildDir(), sourceSet.getName());
+                        final File outputDirectory = new File(outputDirectoryName);
+                        antlrTask.setOutputDirectory(outputDirectory);
+                        sourceSet.getJava().srcDir(outputDirectory);
 
-						//		6) register fact that antlr should be run before compiling
-						project.getTasks().getByName( sourceSet.getCompileJavaTaskName() ).dependsOn( taskName );
-					}
-				}
-		);
+                        // 6) register fact that antlr should be run before compiling
+                        project.getTasks().getByName(sourceSet.getCompileJavaTaskName()).dependsOn(taskName);
+                    }
+                });
     }
 }
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java
index ea987fa..80a0b1e 100644
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectory.java
@@ -16,42 +16,41 @@
 
 package org.gradle.api.plugins.antlr;
 
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.file.FileTree;
 import groovy.lang.Closure;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.file.SourceDirectorySet;
 
 /**
  * Contract for a Gradle "convention object" that acts as a handler for what I call a virtual directory mapping,
- * injecting a virtual directory named 'antlr' into the project's various
- * {@link org.gradle.api.tasks.SourceSet source sets}.  Its implementation gets pushed onto the
- * {@link org.gradle.api.internal.DynamicObjectAware} portion of the source set under the name 'antlr'.
+ * injecting a virtual directory named 'antlr' into the project's various {@link org.gradle.api.tasks.SourceSet source
+ * sets}.  Its implementation gets pushed onto the {@link org.gradle.api.internal.DynamicObjectAware} portion of the
+ * source set under the name 'antlr'.
  *
  * @author Steve Ebersole
  */
 public interface AntlrSourceVirtualDirectory {
-	public static final String NAME = "antlr";
+    public static final String NAME = "antlr";
 
-	/**
-	 * All Antlr source for this source set.
-	 *
-	 * @return The Antlr source.  Never returns null.
-	 */
-	public SourceDirectorySet getAntlr();
+    /**
+     * All Antlr source for this source set.
+     *
+     * @return The Antlr source.  Never returns null.
+     */
+    public SourceDirectorySet getAntlr();
 
-	/**
-	 * Configures the Antlr source for this set. The given closure is used to configure the {@code SourceDirectorySet}
-	 * (see {@link #getAntlr}) which contains the Antlr source.
-	 *
-	 * @param configureClosure The closure to use to configure the Antlr source.
-	 *
-	 * @return this
-	 */
-	public AntlrSourceVirtualDirectory antlr(Closure configureClosure);
+    /**
+     * Configures the Antlr source for this set. The given closure is used to configure the {@code SourceDirectorySet} (see
+     * {@link #getAntlr}) which contains the Antlr source.
+     *
+     * @param configureClosure The closure to use to configure the Antlr source.
+     * @return this
+     */
+    public AntlrSourceVirtualDirectory antlr(Closure configureClosure);
 
-	/**
-	 * All Antlr source for this source set.
-	 *
-	 * @return The Antlr source. Never returns null.
-	 */
-	public FileTree getAllAntlr();
+    /**
+     * All Antlr source for this source set.
+     *
+     * @return The Antlr source. Never returns null.
+     */
+    public FileTree getAllAntlr();
 }
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectoryImpl.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectoryImpl.java
deleted file mode 100644
index fa3a968..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrSourceVirtualDirectoryImpl.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr;
-
-import org.gradle.api.file.SourceDirectorySet;
-import org.gradle.api.internal.file.UnionFileTree;
-import org.gradle.api.internal.file.DefaultSourceDirectorySet;
-import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.tasks.util.PatternFilterable;
-import org.gradle.api.tasks.util.PatternSet;
-import org.gradle.util.ConfigureUtil;
-import groovy.lang.Closure;
-
-/**
- * The implementation of the {@link AntlrSourceVirtualDirectory} contract.
- *
- * @author Steve Ebersole
- */
-public class AntlrSourceVirtualDirectoryImpl implements AntlrSourceVirtualDirectory {
-	private final SourceDirectorySet antlr;
-    private final UnionFileTree allAntlr;
-    private final PatternFilterable antlrPatterns = new PatternSet();
-
-	public AntlrSourceVirtualDirectoryImpl(String parentDisplayName, FileResolver fileResolver) {
-		final String displayName = String.format( "%s Antlr source", parentDisplayName );
-		antlr = new DefaultSourceDirectorySet( displayName, fileResolver );
-		antlr.getFilter().include( "**/*.g" );
-        antlrPatterns.include( "**/*.g" );
-        allAntlr = new UnionFileTree( displayName, antlr.matching( antlrPatterns ) );
-	}
-
-	public SourceDirectorySet getAntlr() {
-		return antlr;
-	}
-
-	public AntlrSourceVirtualDirectory antlr(Closure configureClosure) {
-        ConfigureUtil.configure( configureClosure, getAntlr() );
-        return this;
-    }
-
-	public UnionFileTree getAllAntlr() {
-		return allAntlr;
-	}
-
-	public PatternFilterable getAntlrSourcePatterns() {
-		return antlrPatterns;
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java
index 8aa3783..2c4deb3 100644
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/AntlrTask.java
@@ -22,31 +22,30 @@ import java.util.List;
 import org.apache.tools.ant.taskdefs.optional.ANTLR;
 import org.apache.tools.ant.types.Path;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.plugins.antlr.internal.GenerationPlan;
 import org.gradle.api.tasks.InputFiles;
 import org.gradle.api.tasks.OutputDirectory;
 import org.gradle.api.tasks.SourceTask;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.plugins.antlr.metadata.MetadataExtracter;
-import org.gradle.api.plugins.antlr.metadata.XRef;
-import org.gradle.api.plugins.antlr.plan.GenerationPlan;
-import org.gradle.api.plugins.antlr.plan.GenerationPlanBuilder;
+import org.gradle.api.plugins.antlr.internal.MetadataExtracter;
+import org.gradle.api.plugins.antlr.internal.XRef;
+import org.gradle.api.plugins.antlr.internal.GenerationPlanBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Gradle task for executing Antlr generations.  Wrapper around the Ant {@link ANTLR} task.
- * <p/>
- * Most properties here are self-evident, but I wanted to highlight one in particular:
- * {@link #setAntlrClasspath} is used to define the classpath that should be passed along to the
- * Ant {@link ANTLR} task as its classpath.  That is the classpath it uses to perform generation
- * execution.  This <b>should<b> really only require the antlr jar.  In {@link AntlrPlugin}
- * usage, this would happen simply by adding your antlr jar into the 'antlr' dependency configuration
- * created and exposed by the {@link AntlrPlugin} itself.
+ * <p>Generates parsers from Antlr grammars.  Wrapper around the Ant {@link ANTLR} task.</p>
+ *
+ * <p>Most properties here are self-evident, but I wanted to highlight one in particular: {@link #setAntlrClasspath} is
+ * used to define the classpath that should be passed along to the Ant {@link ANTLR} task as its classpath.  That is the
+ * classpath it uses to perform generation execution.  This <b>should</b> really only require the antlr jar.  In {@link
+ * AntlrPlugin} usage, this would happen simply by adding your antlr jar into the 'antlr' dependency configuration
+ * created and exposed by the {@link AntlrPlugin} itself.</p>
  *
  * @author Steve Ebersole
  */
 public class AntlrTask extends SourceTask {
-	private static final Logger LOGGER = LoggerFactory.getLogger( AntlrTask.class );
+    private static final Logger LOGGER = LoggerFactory.getLogger(AntlrTask.class);
 
     private boolean trace;
     private boolean traceLexer;
@@ -55,89 +54,89 @@ public class AntlrTask extends SourceTask {
 
     private FileCollection antlrClasspath;
 
-	private File outputDirectory;
+    private File outputDirectory;
 
-	public boolean isTrace() {
-		return trace;
-	}
+    public boolean isTrace() {
+        return trace;
+    }
 
-	public void setTrace(boolean trace) {
-		this.trace = trace;
-	}
+    public void setTrace(boolean trace) {
+        this.trace = trace;
+    }
 
-	public boolean isTraceLexer() {
-		return traceLexer;
-	}
+    public boolean isTraceLexer() {
+        return traceLexer;
+    }
 
-	public void setTraceLexer(boolean traceLexer) {
-		this.traceLexer = traceLexer;
-	}
+    public void setTraceLexer(boolean traceLexer) {
+        this.traceLexer = traceLexer;
+    }
 
-	public boolean isTraceParser() {
-		return traceParser;
-	}
+    public boolean isTraceParser() {
+        return traceParser;
+    }
 
-	public void setTraceParser(boolean traceParser) {
-		this.traceParser = traceParser;
-	}
+    public void setTraceParser(boolean traceParser) {
+        this.traceParser = traceParser;
+    }
 
-	public boolean isTraceTreeWalker() {
-		return traceTreeWalker;
-	}
+    public boolean isTraceTreeWalker() {
+        return traceTreeWalker;
+    }
 
-	public void setTraceTreeWalker(boolean traceTreeWalker) {
-		this.traceTreeWalker = traceTreeWalker;
-	}
+    public void setTraceTreeWalker(boolean traceTreeWalker) {
+        this.traceTreeWalker = traceTreeWalker;
+    }
 
     @OutputDirectory
-	public File getOutputDirectory() {
-		return outputDirectory;
-	}
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
 
-	public void setOutputDirectory(File outputDirectory) {
-		this.outputDirectory = outputDirectory;
-	}
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
 
     @InputFiles
-	public FileCollection getAntlrClasspath() {
-		return antlrClasspath;
-	}
-
-	public void setAntlrClasspath(FileCollection antlrClasspath) {
-		this.antlrClasspath = antlrClasspath;
-	}
-
-	@TaskAction
-	public void generate() {
-		// Determine the grammar files and the proper ordering amongst them
-		XRef xref = new MetadataExtracter().extractMetadata( getSource() );
-		List<GenerationPlan> generationPlans = new GenerationPlanBuilder( outputDirectory ).buildGenerationPlans( xref );
-
-		for ( GenerationPlan generationPlan : generationPlans ) {
-			if ( ! generationPlan.isOutOfDate() ) {
-				LOGGER.info( "grammar [" + generationPlan.getId() + "] was up-to-date; skipping" );
-				continue;
-			}
-
-			LOGGER.info( "performing grammar generation [" + generationPlan.getId() + "]" );
-
-			//noinspection ResultOfMethodCallIgnored
-			generationPlan.getGenerationDirectory().mkdirs();
-
-			ANTLR antlr = new ANTLR();
-			antlr.setProject( getAnt().getAntProject() );
-			Path antlrTaskClasspath = antlr.createClasspath();
-			for ( File dep: getAntlrClasspath() ) {
-				antlrTaskClasspath.createPathElement().setLocation( dep );
-			}
-			antlr.setTrace( trace );
-			antlr.setTraceLexer( traceLexer );
-			antlr.setTraceParser( traceParser );
-			antlr.setTraceTreeWalker( traceTreeWalker );
-			antlr.setOutputdirectory( generationPlan.getGenerationDirectory() );
-			antlr.setTarget( generationPlan.getSource() );
-
-			antlr.execute();
-		}
-	}
+    public FileCollection getAntlrClasspath() {
+        return antlrClasspath;
+    }
+
+    public void setAntlrClasspath(FileCollection antlrClasspath) {
+        this.antlrClasspath = antlrClasspath;
+    }
+
+    @TaskAction
+    public void generate() {
+        // Determine the grammar files and the proper ordering amongst them
+        XRef xref = new MetadataExtracter().extractMetadata(getSource());
+        List<GenerationPlan> generationPlans = new GenerationPlanBuilder(outputDirectory).buildGenerationPlans(xref);
+
+        for (GenerationPlan generationPlan : generationPlans) {
+            if (!generationPlan.isOutOfDate()) {
+                LOGGER.info("grammar [" + generationPlan.getId() + "] was up-to-date; skipping");
+                continue;
+            }
+
+            LOGGER.info("performing grammar generation [" + generationPlan.getId() + "]");
+
+            //noinspection ResultOfMethodCallIgnored
+            generationPlan.getGenerationDirectory().mkdirs();
+
+            ANTLR antlr = new ANTLR();
+            antlr.setProject(getAnt().getAntProject());
+            Path antlrTaskClasspath = antlr.createClasspath();
+            for (File dep : getAntlrClasspath()) {
+                antlrTaskClasspath.createPathElement().setLocation(dep);
+            }
+            antlr.setTrace(trace);
+            antlr.setTraceLexer(traceLexer);
+            antlr.setTraceParser(traceParser);
+            antlr.setTraceTreeWalker(traceTreeWalker);
+            antlr.setOutputdirectory(generationPlan.getGenerationDirectory());
+            antlr.setTarget(generationPlan.getSource());
+
+            antlr.execute();
+        }
+    }
 }
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java
new file mode 100644
index 0000000..b891c9a
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/AntlrSourceVirtualDirectoryImpl.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.file.UnionFileTree;
+import org.gradle.api.internal.file.DefaultSourceDirectorySet;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory;
+import org.gradle.api.tasks.util.PatternFilterable;
+import org.gradle.api.tasks.util.PatternSet;
+import org.gradle.util.ConfigureUtil;
+import groovy.lang.Closure;
+
+/**
+ * The implementation of the {@link org.gradle.api.plugins.antlr.AntlrSourceVirtualDirectory} contract.
+ *
+ * @author Steve Ebersole
+ */
+public class AntlrSourceVirtualDirectoryImpl implements AntlrSourceVirtualDirectory {
+    private final SourceDirectorySet antlr;
+    private final UnionFileTree allAntlr;
+    private final PatternFilterable antlrPatterns = new PatternSet();
+
+    public AntlrSourceVirtualDirectoryImpl(String parentDisplayName, FileResolver fileResolver) {
+        final String displayName = String.format("%s Antlr source", parentDisplayName);
+        antlr = new DefaultSourceDirectorySet(displayName, fileResolver);
+        antlr.getFilter().include("**/*.g");
+        antlrPatterns.include("**/*.g");
+        allAntlr = new UnionFileTree(displayName, antlr.matching(antlrPatterns));
+    }
+
+    public SourceDirectorySet getAntlr() {
+        return antlr;
+    }
+
+    public AntlrSourceVirtualDirectory antlr(Closure configureClosure) {
+        ConfigureUtil.configure(configureClosure, getAntlr());
+        return this;
+    }
+
+    public UnionFileTree getAllAntlr() {
+        return allAntlr;
+    }
+
+    public PatternFilterable getAntlrSourcePatterns() {
+        return antlrPatterns;
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java
new file mode 100644
index 0000000..a6b0db7
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlan.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.io.File;
+
+/**
+ * Models information relevant to generation of a particular Antlr grammar file.
+ *
+ * @author Steve Ebersole
+ */
+public class GenerationPlan {
+    private final File source;
+    private final File generationDirectory;
+
+    private File importVocabTokenTypesDirectory;
+    private boolean outOfDate;
+
+    /**
+     * Instantiates a generation plan.
+     *
+     * @param source The grammar file.
+     * @param generationDirectory The directory into which generated lexers and parsers should be written, accounting for
+     * declared package.
+     */
+    GenerationPlan(File source, File generationDirectory) {
+        this.source = source;
+        this.generationDirectory = generationDirectory;
+    }
+
+    public String getId() {
+        return getSource().getPath();
+    }
+
+    public File getSource() {
+        return source;
+    }
+
+    public File getGenerationDirectory() {
+        return generationDirectory;
+    }
+
+    public File getImportVocabTokenTypesDirectory() {
+        return importVocabTokenTypesDirectory;
+    }
+
+    void setImportVocabTokenTypesDirectory(File importVocabTokenTypesDirectory) {
+        this.importVocabTokenTypesDirectory = importVocabTokenTypesDirectory;
+    }
+
+    /**
+     * Is the grammar file modeled by this plan out of considered out of date?
+     *
+     * @return True if the grammar generation is out of date (needs regen); false otherwise.
+     */
+    public boolean isOutOfDate() {
+        return outOfDate;
+    }
+
+    /**
+     * Marks the plan as out of date.
+     */
+    void markOutOfDate() {
+        this.outOfDate = true;
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java
new file mode 100644
index 0000000..0cad3f1
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GenerationPlanBuilder.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.io.File;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builder for the properly order list of {@link GenerationPlan generation plans}.
+ *
+ * <p>IMPL NOTE : Uses recursive calls to achieve ordering.</p>
+ *
+ * @author Steve Ebersole
+ */
+public class GenerationPlanBuilder {
+    private static final Logger LOGGER = LoggerFactory.getLogger(GenerationPlanBuilder.class);
+
+    private final LinkedHashMap<String, GenerationPlan> generationPlans = new LinkedHashMap<String, GenerationPlan>();
+    private final File outputDirectory;
+
+    private XRef metadataXRef;
+
+    public GenerationPlanBuilder(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    public synchronized List<GenerationPlan> buildGenerationPlans(XRef metadataXRef) {
+        this.metadataXRef = metadataXRef;
+
+        Iterator<GrammarFileMetadata> grammarFiles = metadataXRef.iterateGrammarFiles();
+        while (grammarFiles.hasNext()) {
+            final GrammarFileMetadata grammarFileMetadata = grammarFiles.next();
+            // NOTE : loacteOrBuildGenerationPlan populates the generationPlans map
+            loacteOrBuildGenerationPlan(grammarFileMetadata);
+        }
+
+        return new ArrayList<GenerationPlan>(generationPlans.values());
+    }
+
+    private GenerationPlan loacteOrBuildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
+        GenerationPlan generationPlan = generationPlans.get(grammarFileMetadata.getFilePath().getPath());
+        if (generationPlan == null) {
+            generationPlan = buildGenerationPlan(grammarFileMetadata);
+        }
+        return generationPlan;
+    }
+
+    private GenerationPlan buildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
+        File generationDirectory = isEmpty(grammarFileMetadata.getPackageName()) ? outputDirectory : new File(
+                outputDirectory, grammarFileMetadata.getPackageName().replace('.', File.separatorChar));
+
+        GenerationPlan generationPlan = new GenerationPlan(grammarFileMetadata.getFilePath(), generationDirectory);
+
+        for (GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars()) {
+            final File generatedParserFile = new File(outputDirectory, grammarMetadata.determineGeneratedParserPath());
+
+            if (!generatedParserFile.exists()) {
+                generationPlan.markOutOfDate();
+            } else if (generatedParserFile.lastModified() < generationPlan.getSource().lastModified()) {
+                generationPlan.markOutOfDate();
+            }
+
+            // see if the grammar if out-of-date by way of its super-grammar(s) as gleaned from parsing the grammar file
+            if (!grammarMetadata.extendsStandardGrammar()) {
+                final GrammarFileMetadata superGrammarGrammarFileMetadata = grammarMetadata.getSuperGrammarDelegate()
+                        .getAssociatedGrammarMetadata().getGrammarFile();
+                if (superGrammarGrammarFileMetadata != null) {
+                    final GenerationPlan superGrammarGenerationPlan = loacteOrBuildGenerationPlan(
+                            superGrammarGrammarFileMetadata);
+                    if (superGrammarGenerationPlan.isOutOfDate()) {
+                        generationPlan.markOutOfDate();
+                    } else if (superGrammarGenerationPlan.getSource().lastModified() > generatedParserFile
+                            .lastModified()) {
+                        generationPlan.markOutOfDate();
+                    }
+                }
+            }
+
+            // see if the grammar if out-of-date by way of its importVocab
+            if (isNotEmpty(grammarMetadata.getImportVocab())) {
+                final GrammarFileMetadata importVocabGrammarFileMetadata = metadataXRef.getGrammarFileByExportVocab(
+                        grammarMetadata.getImportVocab());
+                if (importVocabGrammarFileMetadata == null) {
+                    LOGGER.warn("unable to locate grammar exporting specifcied import vocab ["
+                            + grammarMetadata.getImportVocab() + "]");
+                } else if (!importVocabGrammarFileMetadata.getFilePath().equals(grammarFileMetadata.getFilePath())) {
+                    final GenerationPlan importVocabGrammarGenerationPlan = loacteOrBuildGenerationPlan(
+                            importVocabGrammarFileMetadata);
+                    generationPlan.setImportVocabTokenTypesDirectory(
+                            importVocabGrammarGenerationPlan.getGenerationDirectory());
+                    if (importVocabGrammarGenerationPlan.isOutOfDate()) {
+                        generationPlan.markOutOfDate();
+                    } else if (importVocabGrammarGenerationPlan.getSource().lastModified() > generatedParserFile
+                            .lastModified()) {
+                        generationPlan.markOutOfDate();
+                    }
+                }
+            }
+        }
+
+        generationPlans.put(generationPlan.getId(), generationPlan);
+        return generationPlan;
+    }
+
+    private boolean isEmpty(String string) {
+        return string == null || string.trim().length() == 0;
+    }
+
+    private boolean isNotEmpty(String string) {
+        return !isEmpty(string);
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java
new file mode 100644
index 0000000..c4f90c6
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarDelegate.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.List;
+
+import antlr.collections.impl.IndexedVector;
+import antlr.preprocessor.GrammarFile;
+
+/**
+ * Antlr defines its {@link antlr.preprocessor.Grammar} class as package-protected for some unfortunate reason. So this
+ * class acts as a delegate to the Antlr {@link antlr.preprocessor.Grammar} class, hiding all the ugly necessary
+ * reflection code.
+ *
+ * @author Steve Ebersole
+ */
+public class GrammarDelegate {
+    public static List<GrammarDelegate> extractGrammarDelegates(GrammarFile antlrGrammarFile) {
+        List<GrammarDelegate> grammarDelegates = new ArrayList<GrammarDelegate>();
+        Enumeration grammarFileGramars = antlrGrammarFile.getGrammars().elements();
+        while (grammarFileGramars.hasMoreElements()) {
+            grammarDelegates.add(new GrammarDelegate(grammarFileGramars.nextElement()));
+        }
+        return grammarDelegates;
+    }
+
+    private final String className;
+    private final String importVocab;
+    private final String exportVocab;
+    private final GrammarDelegate superGrammarDelegate;
+
+    public GrammarDelegate(Object antlrGrammarMetadata) {
+        try {
+            final Method getNameMethod = ANTLR_GRAMMAR_CLASS.getDeclaredMethod("getName", NO_ARG_SIGNATURE);
+            getNameMethod.setAccessible(true);
+            this.className = (String) getNameMethod.invoke(antlrGrammarMetadata, NO_ARGS);
+
+            final Method getSuperGrammarMethod = ANTLR_GRAMMAR_CLASS.getMethod("getSuperGrammar", NO_ARG_SIGNATURE);
+            getSuperGrammarMethod.setAccessible(true);
+            final Object antlrSuperGrammarGrammarMetadata = getSuperGrammarMethod.invoke(antlrGrammarMetadata, NO_ARGS);
+            this.superGrammarDelegate = antlrSuperGrammarGrammarMetadata == null ? null : new GrammarDelegate(
+                    antlrSuperGrammarGrammarMetadata);
+
+            Method getOptionsMethod = ANTLR_GRAMMAR_CLASS.getMethod("getOptions", NO_ARG_SIGNATURE);
+            getOptionsMethod.setAccessible(true);
+            IndexedVector options = (IndexedVector) getOptionsMethod.invoke(antlrGrammarMetadata, NO_ARGS);
+
+            Method getRHSMethod = ANTLR_OPTION_CLASS.getMethod("getRHS", NO_ARG_SIGNATURE);
+            getRHSMethod.setAccessible(true);
+
+            final Object importVocabOption = options == null ? null : options.getElement("importVocab");
+            this.importVocab = importVocabOption == null ? null : vocabName((String) getRHSMethod.invoke(
+                    importVocabOption, NO_ARGS));
+
+            final Object exportVocabOption = options == null ? null : options.getElement("exportVocab");
+            this.exportVocab = exportVocabOption == null ? null : vocabName((String) getRHSMethod.invoke(
+                    exportVocabOption, NO_ARGS));
+        } catch (Throwable t) {
+            throw new IllegalStateException("Error accessing  Antlr grammar metadata", t);
+        }
+    }
+
+    /**
+     * Retrieves the unqualified name of the lexer/parser class.
+     *
+     * @return The unqualified lexer/parser class name.
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * Retrieves the name of this vocabulary imported by this grammar.
+     *
+     * @return The gammar's imported vocabulary name.
+     */
+    public String getImportVocab() {
+        return importVocab;
+    }
+
+    /**
+     * Retrieves the name of this vocabulary exported by this grammar.
+     *
+     * @return The gammar's exported vocabulary name.
+     */
+    public String getExportVocab() {
+        return exportVocab;
+    }
+
+    /**
+     * Retrieves the grammar delegate associated with this grammars super grammar deduced during preprocessing from its
+     * extends clause.
+     *
+     * @return The super-grammar grammar delegate
+     */
+    public GrammarDelegate getSuperGrammarDelegate() {
+        return superGrammarDelegate;
+    }
+
+    private GrammarMetadata associatedGrammarMetadata;
+
+    public void associateWith(GrammarMetadata associatedGrammarMetadata) {
+        this.associatedGrammarMetadata = associatedGrammarMetadata;
+    }
+
+    public GrammarMetadata getAssociatedGrammarMetadata() {
+        return associatedGrammarMetadata;
+    }
+
+    private String vocabName(String vocabName) {
+        if (vocabName == null) {
+            return null;
+        }
+        vocabName = vocabName.trim();
+        if (vocabName.endsWith(";")) {
+            vocabName = vocabName.substring(0, vocabName.length() - 1);
+        }
+        return vocabName;
+    }
+
+    private static final Class ANTLR_GRAMMAR_CLASS;
+    private static final Class ANTLR_OPTION_CLASS;
+
+    static {
+        ANTLR_GRAMMAR_CLASS = loadAntlrClass("antlr.preprocessor.Grammar");
+        ANTLR_OPTION_CLASS = loadAntlrClass("antlr.preprocessor.Option");
+    }
+
+    public static final Class[] NO_ARG_SIGNATURE = new Class[0];
+    public static final Object[] NO_ARGS = new Object[0];
+
+    private static Class loadAntlrClass(String className) {
+        try {
+            return Class.forName(className, true, GrammarDelegate.class.getClassLoader());
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException("Unable to locate Antlr class [" + className + "]", e );
+        }
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java
new file mode 100644
index 0000000..0ef041f
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarFileMetadata.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.File;
+
+/**
+ * Models information about an Antlr grammar file, including the inidividual {@link #getGrammars grammars} (lexers,
+ * parsers, etc) contained within it.
+ *
+ * @author Steve Ebersole
+ */
+public class GrammarFileMetadata {
+    private final File filePath;
+    private final antlr.preprocessor.GrammarFile antlrGrammarFile;
+    private final String packageName;
+    private List<GrammarMetadata> grammarMetadatas = new ArrayList<GrammarMetadata>();
+
+    public GrammarFileMetadata(File filePath, antlr.preprocessor.GrammarFile antlrGrammarFile, String packageName) {
+        this.filePath = filePath;
+        this.antlrGrammarFile = antlrGrammarFile;
+        this.packageName = packageName;
+
+        List<GrammarDelegate> antlrGrammarDelegates = GrammarDelegate.extractGrammarDelegates(antlrGrammarFile);
+        for (GrammarDelegate antlrGrammarDelegate : antlrGrammarDelegates) {
+            GrammarMetadata grammarMetadata = new GrammarMetadata(this, antlrGrammarDelegate);
+            grammarMetadatas.add(grammarMetadata);
+        }
+    }
+
+    public File getFilePath() {
+        return filePath;
+    }
+
+    public antlr.preprocessor.GrammarFile getAntlrGrammarFile() {
+        return antlrGrammarFile;
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public List<GrammarMetadata> getGrammars() {
+        return grammarMetadatas;
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java
new file mode 100644
index 0000000..796370f
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/GrammarMetadata.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.io.File;
+
+import antlr.TreeParser;
+import antlr.Parser;
+
+/**
+ * Models a grammar defined within an Antlr grammar file.
+ *
+ * @author Steve Ebersole
+ */
+public class GrammarMetadata {
+    private final GrammarFileMetadata grammarFileMetadata;
+    private final GrammarDelegate grammarDelegate;
+
+    public GrammarMetadata(GrammarFileMetadata grammarFileMetadata, GrammarDelegate grammarDelegate) {
+        this.grammarFileMetadata = grammarFileMetadata;
+        this.grammarDelegate = grammarDelegate;
+        grammarDelegate.associateWith(this);
+    }
+
+    public GrammarFileMetadata getGrammarFile() {
+        return grammarFileMetadata;
+    }
+
+    public String getClassName() {
+        return grammarDelegate.getClassName();
+    }
+
+    public String getQualifiedClassName() {
+        if (isEmpty(getPackageName())) {
+            return getClassName();
+        } else {
+            return getPackageName() + '.' + getClassName();
+        }
+    }
+
+    public GrammarDelegate getSuperGrammarDelegate() {
+        return grammarDelegate.getSuperGrammarDelegate();
+    }
+
+    public boolean extendsStandardGrammar() {
+        final String superGrammarClassName = getSuperGrammarDelegate().getClassName();
+        return Parser.class.getName().equals(superGrammarClassName) || Parser.class.getSimpleName().equals(
+                superGrammarClassName) || TreeParser.class.getName().equals(superGrammarClassName)
+                || TreeParser.class.getSimpleName().equals(superGrammarClassName) || "Lexer".equals(
+                superGrammarClassName);
+    }
+
+    public String getImportVocab() {
+        return grammarDelegate.getImportVocab();
+    }
+
+    public String getExportVocab() {
+        return grammarDelegate.getExportVocab();
+    }
+
+    public String getPackageName() {
+        return getGrammarFile().getPackageName();
+    }
+
+    /**
+     * Determine the relative path of the generated parser java file.
+     *
+     * @return The relative generated parser file path.
+     */
+    public String determineGeneratedParserPath() {
+        if (isEmpty(getPackageName())) {
+            return getClassName() + ".java";
+        } else {
+            return getPackageName().replace('.', File.separatorChar) + File.separatorChar + getClassName() + ".java";
+        }
+    }
+
+    private boolean isEmpty(String packageName) {
+        return packageName == null || packageName.trim().length() == 0;
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java
new file mode 100644
index 0000000..35e480f
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/MetadataExtracter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.file.FileTree;
+
+/**
+ * Preprocess an Antlr grammar file so that dependencies between grammars can be properly determined such that they can
+ * be processed for generation in proper order later.
+ *
+ * @author Steve Ebersole
+ */
+public class MetadataExtracter {
+
+    public XRef extractMetadata(FileTree source) {
+        antlr.Tool tool = new antlr.Tool();
+        antlr.preprocessor.Hierarchy hierarchy = new antlr.preprocessor.Hierarchy(tool);
+
+        // first let antlr preprocess the grammars...
+        for (File grammarFileFile : source.getFiles()) {
+            final String grammarFilePath = grammarFileFile.getPath();
+
+            try {
+                hierarchy.readGrammarFile(grammarFilePath);
+            } catch (FileNotFoundException e) {
+                // should never happen here
+                throw new IllegalStateException("Received FileNotFoundException on already read file", e);
+            }
+        }
+
+        // now, do our processing using the antlr preprocessor results whenever possible.
+        XRef xref = new XRef(hierarchy);
+        for (File grammarFileFile : source.getFiles()) {
+
+            // determine the package name :(
+            String grammarPackageName = null;
+            try {
+                BufferedReader in = new BufferedReader(new FileReader(grammarFileFile));
+                try {
+                    String line;
+                    while ((line = in.readLine()) != null) {
+                        line = line.trim();
+                        if (line.startsWith("package") && line.endsWith(";")) {
+                            grammarPackageName = line.substring(8, line.length() - 1);
+                            break;
+                        }
+                    }
+                } finally {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+            final String grammarFilePath = grammarFileFile.getPath();
+            antlr.preprocessor.GrammarFile antlrGrammarFile = hierarchy.getFile(grammarFilePath);
+
+            GrammarFileMetadata grammarFileMetadata = new GrammarFileMetadata(grammarFileFile, antlrGrammarFile,
+                    grammarPackageName);
+
+            xref.addGrammarFile(grammarFileMetadata);
+        }
+
+        return xref;
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java
new file mode 100644
index 0000000..b502211
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/internal/XRef.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.plugins.antlr.internal;
+
+import java.util.LinkedHashMap;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import antlr.preprocessor.Hierarchy;
+
+/**
+ * Models cross-reference (x-ref) info about {@link GrammarFileMetadata grammar files} such as {@link #filesByPath},
+ * {@link #filesByExportVocab} and {@link #filesByClassName}.
+ *
+ * @author Steve Ebersole
+ */
+public class XRef {
+    private final Hierarchy antlrHierarchy;
+
+    private LinkedHashMap<String, GrammarFileMetadata> filesByPath = new LinkedHashMap<String, GrammarFileMetadata>();
+    private HashMap<String, GrammarFileMetadata> filesByExportVocab = new HashMap<String, GrammarFileMetadata>();
+    private HashMap<String, GrammarFileMetadata> filesByClassName = new HashMap<String, GrammarFileMetadata>();
+
+    public XRef(Hierarchy antlrHierarchy) {
+        this.antlrHierarchy = antlrHierarchy;
+    }
+
+    public Object getAntlrHierarchy() {
+        return antlrHierarchy;
+    }
+
+    /**
+     * Adds a grammar file to this cross-reference.
+     *
+     * @param grammarFileMetadata The grammar file to add (and to be cross referenced).
+     */
+    void addGrammarFile(GrammarFileMetadata grammarFileMetadata) {
+        filesByPath.put(grammarFileMetadata.getFilePath().getPath(), grammarFileMetadata);
+        for (GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars()) {
+            filesByClassName.put(grammarMetadata.getClassName(), grammarFileMetadata);
+            if (grammarMetadata.getExportVocab() != null) {
+                GrammarFileMetadata old = filesByExportVocab.put(grammarMetadata.getExportVocab(), grammarFileMetadata);
+                if (old != null && old != grammarFileMetadata) {
+                    System.out.println("[WARNING] : multiple grammars defined the same exportVocab : " + grammarMetadata
+                            .getExportVocab());
+                }
+            }
+        }
+    }
+
+    public Iterator<GrammarFileMetadata> iterateGrammarFiles() {
+        return filesByPath.values().iterator();
+    }
+
+    /**
+     * Locate the grammar file metadata by grammar file path.
+     *
+     * @param path The grammar file path.
+     * @return The grammar file metadata.  May be null if none found.
+     */
+    public GrammarFileMetadata getGrammarFileByPath(String path) {
+        return filesByPath.get(path);
+    }
+
+    /**
+     * Locate the grammar file metadata by the name of a class generated from one of its included grammars.
+     *
+     * @param className The generated class name.
+     * @return The grammar file metadata.  May be null if none found.
+     */
+    public GrammarFileMetadata getGrammarFileByClassName(String className) {
+        return filesByClassName.get(className);
+    }
+
+    /**
+     * Locate the grammar file metadata by the name of a vocabulary exported from one of its included grammars.
+     *
+     * @param vocabName The vocabulary name
+     * @return The grammar file metadata.  May be null if none found.
+     */
+    public GrammarFileMetadata getGrammarFileByExportVocab(String vocabName) {
+        return filesByExportVocab.get(vocabName );
+    }
+}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarDelegate.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarDelegate.java
deleted file mode 100644
index 09d6052..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarDelegate.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.metadata;
-
-import java.lang.reflect.Method;
-import java.util.Enumeration;
-import java.util.ArrayList;
-import java.util.List;
-
-import antlr.collections.impl.IndexedVector;
-import antlr.preprocessor.GrammarFile;
-
-/**
- * Antlr defines its {@link antlr.preprocessor.Grammar} class as package-protected for some unfortunate reason.
- * So this class acts as a delegate to the Antlr {@link antlr.preprocessor.Grammar} class, hiding all the
- * ugly necessary reflection code.
- *
- * @author Steve Ebersole
- */
-public class GrammarDelegate {
-	public static List<GrammarDelegate> extractGrammarDelegates(GrammarFile antlrGrammarFile) {
-		List<GrammarDelegate> grammarDelegates = new ArrayList<GrammarDelegate>();
-		Enumeration grammarFileGramars = antlrGrammarFile.getGrammars().elements();
-		while ( grammarFileGramars.hasMoreElements() ) {
-			grammarDelegates.add( new GrammarDelegate( grammarFileGramars.nextElement() ) );
-		}
-		return grammarDelegates;
-	}
-
-	private final String className;
-	private final String importVocab;
-	private final String exportVocab;
-	private final GrammarDelegate superGrammarDelegate;
-
-	public GrammarDelegate(Object antlrGrammarMetadata) {
-		try {
-			final Method getNameMethod = ANTLR_GRAMMAR_CLASS.getDeclaredMethod( "getName", NO_ARG_SIGNATURE );
-			getNameMethod.setAccessible( true );
-			this.className = ( String ) getNameMethod.invoke( antlrGrammarMetadata, NO_ARGS );
-
-			final Method getSuperGrammarMethod = ANTLR_GRAMMAR_CLASS.getMethod( "getSuperGrammar", NO_ARG_SIGNATURE );
-			getSuperGrammarMethod.setAccessible( true );
-			final Object antlrSuperGrammarGrammarMetadata = getSuperGrammarMethod.invoke( antlrGrammarMetadata, NO_ARGS );
-			this.superGrammarDelegate = antlrSuperGrammarGrammarMetadata == null
-					? null
-					: new GrammarDelegate( antlrSuperGrammarGrammarMetadata );
-
-			Method getOptionsMethod = ANTLR_GRAMMAR_CLASS.getMethod( "getOptions", NO_ARG_SIGNATURE );
-			getOptionsMethod.setAccessible( true );
-			IndexedVector options = ( IndexedVector ) getOptionsMethod.invoke( antlrGrammarMetadata, NO_ARGS );
-
-			Method getRHSMethod = ANTLR_OPTION_CLASS.getMethod( "getRHS", NO_ARG_SIGNATURE );
-			getRHSMethod.setAccessible( true );
-
-			final Object importVocabOption = options == null
-					? null
-					: options.getElement( "importVocab" );
-			this.importVocab = importVocabOption == null
-					? null
-					: vocabName( (String) getRHSMethod.invoke( importVocabOption, NO_ARGS ) );
-
-			final Object exportVocabOption = options == null
-					? null
-					: options.getElement( "exportVocab" );
-			this.exportVocab = exportVocabOption == null
-					? null
-					: vocabName( (String) getRHSMethod.invoke( exportVocabOption, NO_ARGS ) );
-		}
-		catch ( Throwable t ) {
-			throw new IllegalStateException( "Error accessing  Antlr grammar metadata", t );
-		}
-	}
-
-	/**
-	 * Retrieves the unqualified name of the lexer/parser class.
-	 *
-	 * @return The unqualified lexer/parser class name.
-	 */
-	public String getClassName() {
-		return className;
-	}
-
-	/**
-	 * Retrieves the name of this vocabulary imported by this grammar.
-	 *
-	 * @return The gammar's imported vocabulary name.
-	 */
-	public String getImportVocab() {
-		return importVocab;
-	}
-
-	/**
-	 * Retrieves the name of this vocabulary exported by this grammar.
-	 *
-	 * @return The gammar's exported vocabulary name.
-	 */
-	public String getExportVocab() {
-		return exportVocab;
-	}
-
-	/**
-	 * Retrieves the grammar delegate associated with this grammars super grammar deduced during preprocessing
-	 * from its extends clause.
-	 *
-	 * @return The super-grammar grammar delegate
-	 */
-	public GrammarDelegate getSuperGrammarDelegate() {
-		return superGrammarDelegate;
-	}
-
-	private GrammarMetadata associatedGrammarMetadata;
-
-	public void associateWith(GrammarMetadata associatedGrammarMetadata) {
-		this.associatedGrammarMetadata = associatedGrammarMetadata;
-	}
-
-	public GrammarMetadata getAssociatedGrammarMetadata() {
-		return associatedGrammarMetadata;
-	}
-
-	private String vocabName(String vocabName) {
-		if ( vocabName == null ) {
-			return null;
-		}
-		vocabName = vocabName.trim();
-		if ( vocabName.endsWith( ";" ) ) {
-			vocabName = vocabName.substring( 0, vocabName.length() - 1 );
-		}
-		return vocabName;
-	}
-
-	private static final Class ANTLR_GRAMMAR_CLASS;
-	private static final Class ANTLR_OPTION_CLASS;
-
-	static {
-		ANTLR_GRAMMAR_CLASS = loadAntlrClass( "antlr.preprocessor.Grammar" );
-		ANTLR_OPTION_CLASS = loadAntlrClass( "antlr.preprocessor.Option" );
-	}
-
-	public static final Class[] NO_ARG_SIGNATURE = new Class[0];
-	public static final Object[] NO_ARGS = new Object[0];
-
-	private static Class loadAntlrClass(String className) {
-		try {
-			return Class.forName( className, true, GrammarDelegate.class.getClassLoader() );
-		}
-		catch ( ClassNotFoundException e ) {
-			throw new IllegalStateException( "Unable to locate Antlr class [" + className + "]", e );
-		}
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarFileMetadata.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarFileMetadata.java
deleted file mode 100644
index be07239..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarFileMetadata.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.metadata;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.File;
-
-/**
- * Models information about an Antlr grammar file, including the inidividual {@link #getGrammars grammars}
- * (lexers, parsers, etc) contained within it.
- *
- * @author Steve Ebersole
- */
-public class GrammarFileMetadata {
-	private final File filePath;
-	private final antlr.preprocessor.GrammarFile antlrGrammarFile;
-	private final String packageName;
-	private List<GrammarMetadata> grammarMetadatas = new ArrayList<GrammarMetadata>();
-
-	public GrammarFileMetadata(File filePath, antlr.preprocessor.GrammarFile antlrGrammarFile, String packageName) {
-		this.filePath = filePath;
-		this.antlrGrammarFile = antlrGrammarFile;
-		this.packageName = packageName;
-
-		List<GrammarDelegate> antlrGrammarDelegates = GrammarDelegate.extractGrammarDelegates( antlrGrammarFile );
-		for ( GrammarDelegate antlrGrammarDelegate : antlrGrammarDelegates ) {
-			GrammarMetadata grammarMetadata = new GrammarMetadata( this, antlrGrammarDelegate );
-			grammarMetadatas.add( grammarMetadata );
-		}
-	}
-
-	public File getFilePath() {
-		return filePath;
-	}
-
-	public antlr.preprocessor.GrammarFile getAntlrGrammarFile() {
-		return antlrGrammarFile;
-	}
-
-	public String getPackageName() {
-		return packageName;
-	}
-
-	public List<GrammarMetadata> getGrammars() {
-		return grammarMetadatas;
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarMetadata.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarMetadata.java
deleted file mode 100644
index 5df1c0c..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/GrammarMetadata.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.metadata;
-
-import java.io.File;
-
-import antlr.TreeParser;
-import antlr.Parser;
-
-/**
- * Models a grammar defined within an Antlr grammar file.
- *
- * @author Steve Ebersole
- */
-public class GrammarMetadata {
-	private final GrammarFileMetadata grammarFileMetadata;
-	private final GrammarDelegate grammarDelegate;
-
-	public GrammarMetadata(GrammarFileMetadata grammarFileMetadata, GrammarDelegate grammarDelegate) {
-		this.grammarFileMetadata = grammarFileMetadata;
-		this.grammarDelegate = grammarDelegate;
-		grammarDelegate.associateWith( this );
-	}
-
-	public GrammarFileMetadata getGrammarFile() {
-		return grammarFileMetadata;
-	}
-
-	public String getClassName() {
-		return grammarDelegate.getClassName();
-	}
-
-	public String getQualifiedClassName() {
-		if ( isEmpty( getPackageName() ) ) {
-			return getClassName();
-		}
-		else {
-			return getPackageName() + '.' + getClassName();
-		}
-	}
-
-	public GrammarDelegate getSuperGrammarDelegate() {
-		return grammarDelegate.getSuperGrammarDelegate();
-	}
-
-	public boolean extendsStandardGrammar() {
-		final String superGrammarClassName = getSuperGrammarDelegate().getClassName();
-		return Parser.class.getName().equals( superGrammarClassName )
-				|| Parser.class.getSimpleName().equals( superGrammarClassName )
-				|| TreeParser.class.getName().equals( superGrammarClassName )
-				|| TreeParser.class.getSimpleName().equals( superGrammarClassName )
-				|| "Lexer".equals( superGrammarClassName );
-	}
-
-	public String getImportVocab() {
-		return grammarDelegate.getImportVocab();
-	}
-
-	public String getExportVocab() {
-		return grammarDelegate.getExportVocab();
-	}
-
-	public String getPackageName() {
-		return getGrammarFile().getPackageName();
-	}
-
-	/**
-	 * Determine the relative path of the generated parser java file.
-	 * 
-	 * @return The relative generated parser file path.
-	 */
-	public String determineGeneratedParserPath() {
-		if ( isEmpty( getPackageName() ) ) {
-			return getClassName() + ".java";
-		}
-		else {
-			return getPackageName().replace( '.', File.separatorChar )
-					+ File.separatorChar
-					+ getClassName()
-					+ ".java";
-		}
-	}
-
-	private boolean isEmpty(String packageName) {
-		return packageName == null || packageName.trim().length() == 0;
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/MetadataExtracter.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/MetadataExtracter.java
deleted file mode 100644
index 0dd1d73..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/MetadataExtracter.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.metadata;
-
-import java.io.File;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.file.FileTree;
-
-/**
- * Preprocess an Antlr grammar file so that dependencies between grammars
- * can be properly determined such that they can be processed for generation
- * in proper order later.
- *
- * @author Steve Ebersole
- */
-public class MetadataExtracter {
-
-	public XRef extractMetadata(FileTree source) {
-		antlr.Tool tool = new antlr.Tool();
-		antlr.preprocessor.Hierarchy hierarchy = new antlr.preprocessor.Hierarchy( tool );
-
-		// first let antlr preprocess the grammars...
-		for ( File grammarFileFile : source.getFiles() ) {
-			final String grammarFilePath = grammarFileFile.getPath();
-
-			try {
-				hierarchy.readGrammarFile( grammarFilePath );
-			}
-			catch ( FileNotFoundException e ) {
-				// should never happen here
-				throw new IllegalStateException( "Received FileNotFoundException on already read file", e );
-			}
-		}
-
-		// now, do our processing using the antlr preprocessor results whenever possible.
-		XRef xref = new XRef( hierarchy );
-		for ( File grammarFileFile : source.getFiles() ) {
-
-			// determine the package name :(
-			String grammarPackageName = null;
-			try {
-				BufferedReader in = new BufferedReader( new FileReader( grammarFileFile ) );
-				try {
-					String line;
-					while ( ( line = in.readLine() ) != null ) {
-						line = line.trim();
-						if ( line.startsWith( "package" ) && line.endsWith( ";" ) ) {
-							grammarPackageName = line.substring( 8, line.length() -1 );
-							break;
-						}
-					}
-				}
-				finally {
-					try {
-						in.close();
-					}
-					catch ( IOException e ) {
-                        throw new UncheckedIOException(e);
-                    }
-				}
-			}
-			catch ( IOException e ) {
-				e.printStackTrace();
-			}
-
-			final String grammarFilePath = grammarFileFile.getPath();
-			antlr.preprocessor.GrammarFile antlrGrammarFile = hierarchy.getFile( grammarFilePath );
-
-			GrammarFileMetadata grammarFileMetadata = new GrammarFileMetadata(
-					grammarFileFile,
-					antlrGrammarFile,
-					grammarPackageName
-			);
-
-			xref.addGrammarFile( grammarFileMetadata );
-		}
-
-		return xref;
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/XRef.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/XRef.java
deleted file mode 100644
index 40a4a55..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/metadata/XRef.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.metadata;
-
-import java.util.LinkedHashMap;
-import java.util.HashMap;
-import java.util.Iterator;
-
-import antlr.preprocessor.Hierarchy;
-
-/**
- * Models cross-reference (x-ref) info about {@link GrammarFileMetadata grammar files} such as {@link #filesByPath},
- * {@link #filesByExportVocab} and {@link #filesByClassName}.
- *
- * @author Steve Ebersole
- */
-public class XRef {
-	private final Hierarchy antlrHierarchy;
-
-	private LinkedHashMap<String, GrammarFileMetadata> filesByPath = new LinkedHashMap<String, GrammarFileMetadata>();
-	private HashMap<String, GrammarFileMetadata> filesByExportVocab = new HashMap<String, GrammarFileMetadata>();
-	private HashMap<String, GrammarFileMetadata> filesByClassName = new HashMap<String, GrammarFileMetadata>();
-
-	public XRef(Hierarchy antlrHierarchy) {
-		this.antlrHierarchy = antlrHierarchy;
-	}
-
-	public Object getAntlrHierarchy() {
-		return antlrHierarchy;
-	}
-
-	/**
-	 * Adds a grammar file to this cross-reference.
-	 * 
-	 * @param grammarFileMetadata The grammar file to add (and to be cross referenced).
-	 */
-	void addGrammarFile(GrammarFileMetadata grammarFileMetadata) {
-		filesByPath.put( grammarFileMetadata.getFilePath().getPath(), grammarFileMetadata );
-		for ( GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars() ) {
-			filesByClassName.put( grammarMetadata.getClassName(), grammarFileMetadata );
-			if ( grammarMetadata.getExportVocab() != null ) {
-				GrammarFileMetadata old = filesByExportVocab.put( grammarMetadata.getExportVocab(), grammarFileMetadata );
-				if ( old != null && old != grammarFileMetadata ) {
-					System.out.println( "[WARNING] : multiple grammars defined the same exportVocab : " + grammarMetadata
-							.getExportVocab() );
-				}
-			}
-		}
-	}
-
-	public Iterator<GrammarFileMetadata> iterateGrammarFiles() {
-		return filesByPath.values().iterator();
-	}
-
-	/**
-	 * Locate the grammar file metadata by grammar file path.
-	 *
-	 * @param path The grammar file path.
-	 * @return The grammar file metadata.  May be null if none found.
-	 */
-	public GrammarFileMetadata getGrammarFileByPath(String path) {
-		return filesByPath.get( path );
-	}
-
-	/**
-	 * Locate the grammar file metadata by the name of a class generated from one of its included grammars.
-	 *
-	 * @param className The generated class name.
-	 * @return The grammar file metadata.  May be null if none found.
-	 */
-	public GrammarFileMetadata getGrammarFileByClassName(String className) {
-		return filesByClassName.get( className );
-	}
-
-	/**
-	 * Locate the grammar file metadata by the name of a vocabulary exported from one of its included grammars.
-	 *
-	 * @param vocabName The vocabulary name
-	 * @return The grammar file metadata.  May be null if none found.
-	 */
-	public GrammarFileMetadata getGrammarFileByExportVocab(String vocabName) {
-		return filesByExportVocab.get( vocabName );
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java
new file mode 100644
index 0000000..8378926
--- /dev/null
+++ b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A {link org.gradle.api.Plugin} for generating parsers from Antlr grammars.
+ */
+package org.gradle.api.plugins.antlr;
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/plan/GenerationPlan.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/plan/GenerationPlan.java
deleted file mode 100644
index ba176ac..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/plan/GenerationPlan.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.plan;
-
-import java.io.File;
-
-/**
- * Models information relevant to generation of a particular Antlr grammar file.
- *
- * @author Steve Ebersole
- */
-public class GenerationPlan {
-	private final File source;
-	private final File generationDirectory;
-
-	private File importVocabTokenTypesDirectory;
-	private boolean outOfDate;
-
-	/**
-	 * Instantiates a generation plan.
-	 *
-	 * @param source The grammar file.
-	 * @param generationDirectory The directory into which generated lexers and parsers should be written, accounting
-	 * for delared package.
-	 */
-	GenerationPlan(File source, File generationDirectory) {
-		this.source = source;
-		this.generationDirectory = generationDirectory;
-	}
-
-	public String getId() {
-		return getSource().getPath();
-	}
-
-	public File getSource() {
-		return source;
-	}
-
-	public File getGenerationDirectory() {
-		return generationDirectory;
-	}
-
-	public File getImportVocabTokenTypesDirectory() {
-		return importVocabTokenTypesDirectory;
-	}
-
-	void setImportVocabTokenTypesDirectory(File importVocabTokenTypesDirectory) {
-		this.importVocabTokenTypesDirectory = importVocabTokenTypesDirectory;
-	}
-
-	/**
-	 * Is the grammar file modeled by this plan out of considered out of date?
-	 *
-	 * @return True if the grammar generation is out of date (needs regen); false otherwise.
-	 */
-	public boolean isOutOfDate() {
-		return outOfDate;
-	}
-
-	/**
-	 * Marks the plan as out of date.
-	 */
-	void markOutOfDate() {
-		this.outOfDate = true;
-	}
-}
diff --git a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/plan/GenerationPlanBuilder.java b/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/plan/GenerationPlanBuilder.java
deleted file mode 100644
index 4701564..0000000
--- a/subprojects/gradle-antlr/src/main/groovy/org/gradle/api/plugins/antlr/plan/GenerationPlanBuilder.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins.antlr.plan;
-
-import java.io.File;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Iterator;
-import java.util.ArrayList;
-
-import org.gradle.api.plugins.antlr.metadata.XRef;
-import org.gradle.api.plugins.antlr.metadata.GrammarFileMetadata;
-import org.gradle.api.plugins.antlr.metadata.GrammarMetadata;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Builder for the properly order list of {@link GenerationPlan generation plans}.
- * <p/>
- * IMPL NOTE : Uses recursive calls to achieve ordering.
- *
- * @author Steve Ebersole
- */
-public class GenerationPlanBuilder {
-	private static final Logger LOGGER = LoggerFactory.getLogger( GenerationPlanBuilder.class );
-
-	private final LinkedHashMap<String,GenerationPlan> generationPlans = new LinkedHashMap<String,GenerationPlan>();
-	private final File outputDirectory;
-
-	private XRef metadataXRef;
-
-	public GenerationPlanBuilder(File outputDirectory) {
-		this.outputDirectory = outputDirectory;
-	}
-
-	public synchronized List<GenerationPlan> buildGenerationPlans(XRef metadataXRef) {
-		this.metadataXRef = metadataXRef;
-
-		Iterator<GrammarFileMetadata> grammarFiles = metadataXRef.iterateGrammarFiles();
-		while ( grammarFiles.hasNext() ) {
-			final GrammarFileMetadata grammarFileMetadata = grammarFiles.next();
-			// NOTE : loacteOrBuildGenerationPlan populates the generationPlans map
-			loacteOrBuildGenerationPlan( grammarFileMetadata );
-		}
-
-		return new ArrayList<GenerationPlan>( generationPlans.values() );
-	}
-
-	private GenerationPlan loacteOrBuildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
-		GenerationPlan generationPlan = generationPlans.get( grammarFileMetadata.getFilePath().getPath() );
-		if ( generationPlan == null ) {
-			generationPlan = buildGenerationPlan( grammarFileMetadata );
-		}
-		return generationPlan;
-	}
-
-	private GenerationPlan buildGenerationPlan(GrammarFileMetadata grammarFileMetadata) {
-		File generationDirectory = isEmpty( grammarFileMetadata.getPackageName() )
-				? outputDirectory
-				: new File( outputDirectory, grammarFileMetadata.getPackageName().replace( '.', File.separatorChar ) );
-
-		GenerationPlan generationPlan = new GenerationPlan(
-				grammarFileMetadata.getFilePath(),
-				generationDirectory
-		);
-
-		for ( GrammarMetadata grammarMetadata : grammarFileMetadata.getGrammars() ) {
-			final File generatedParserFile = new File(
-					outputDirectory,
-					grammarMetadata.determineGeneratedParserPath()
-			);
-
-			if ( !generatedParserFile.exists() ) {
-				generationPlan.markOutOfDate();
-			}
-			else if ( generatedParserFile.lastModified() < generationPlan.getSource().lastModified() ) {
-				generationPlan.markOutOfDate();
-			}
-
-			// see if the grammar if out-of-date by way of its super-grammar(s) as gleaned from parsing the grammar file
-			if ( !grammarMetadata.extendsStandardGrammar() ) {
-				final GrammarFileMetadata superGrammarGrammarFileMetadata = grammarMetadata.getSuperGrammarDelegate()
-						.getAssociatedGrammarMetadata()
-						.getGrammarFile();
-				if ( superGrammarGrammarFileMetadata != null ) {
-					final GenerationPlan superGrammarGenerationPlan = loacteOrBuildGenerationPlan(
-							superGrammarGrammarFileMetadata
-					);
-					if ( superGrammarGenerationPlan.isOutOfDate() ) {
-						generationPlan.markOutOfDate();
-					}
-					else if ( superGrammarGenerationPlan.getSource().lastModified()
-							> generatedParserFile.lastModified() ) {
-						generationPlan.markOutOfDate();
-					}
-				}
-			}
-
-			// see if the grammar if out-of-date by way of its importVocab
-			if ( isNotEmpty( grammarMetadata.getImportVocab() ) ) {
-				final GrammarFileMetadata importVocabGrammarFileMetadata = metadataXRef.getGrammarFileByExportVocab( grammarMetadata.getImportVocab() );
-				if ( importVocabGrammarFileMetadata == null ) {
-					LOGGER.warn( "unable to locate grammar exporting specifcied import vocab [" + grammarMetadata.getImportVocab() + "]" );
-				}
-				else if ( !importVocabGrammarFileMetadata.getFilePath().equals( grammarFileMetadata.getFilePath() ) ) {
-					final GenerationPlan importVocabGrammarGenerationPlan = loacteOrBuildGenerationPlan(
-							importVocabGrammarFileMetadata
-					);
-					generationPlan.setImportVocabTokenTypesDirectory(
-							importVocabGrammarGenerationPlan.getGenerationDirectory()
-					);
-					if ( importVocabGrammarGenerationPlan.isOutOfDate() ) {
-						generationPlan.markOutOfDate();
-					}
-					else if ( importVocabGrammarGenerationPlan.getSource()
-							.lastModified() > generatedParserFile.lastModified() ) {
-						generationPlan.markOutOfDate();
-					}
-				}
-			}
-		}
-
-		generationPlans.put( generationPlan.getId(), generationPlan );
-		return generationPlan;
-	}
-
-	private boolean isEmpty(String string) {
-		return string == null || string.trim().length() == 0;
-	}
-
-	private boolean isNotEmpty(String string) {
-		return ! isEmpty( string );
-	}
-
-}
diff --git a/subprojects/gradle-code-quality/code-quality.gradle b/subprojects/gradle-code-quality/code-quality.gradle
index 591e09d..a0b5d69 100644
--- a/subprojects/gradle-code-quality/code-quality.gradle
+++ b/subprojects/gradle-code-quality/code-quality.gradle
@@ -19,19 +19,19 @@ dependencies {
     compile project(':core')
     compile project(':plugins')
 
-    compile "org.codenarc:CodeNarc:0.9 at jar"
+    compile "org.codenarc:CodeNarc:0.10 at jar"
     compile libraries.slf4j_api
- 
+
+    // CodeNarc dependencies
     runtime libraries.log4j_to_slf4j,
-            "org.gmetrics:GMetrics:0.2 at jar"
+            "org.gmetrics:GMetrics:0.3 at jar"
 
-    def BEAN_UTILS = ["commons-beanutils:commons-beanutils-core:1.7.0 at jar", "commons-collections:commons-collections:2.0 at jar", libraries.jcl_to_slf4j]
-    runtime "checkstyle:checkstyle:5.1 at jar",
-            libraries.antlr,
-            libraries.jcl_to_slf4j,
+    // Checkstyle dependencies
+    runtime "com.puppycrawl.tools:checkstyle:5.2 at jar",
             libraries.google_collections,
-            BEAN_UTILS
-    
+            libraries.antlr,
+            "commons-beanutils:commons-beanutils-core:1.8.3 at jar"
+
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
 }
diff --git a/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java b/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java
index b1d060b..4a11975 100644
--- a/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java
+++ b/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/Checkstyle.java
@@ -22,6 +22,9 @@ import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
+/**
+ * Runs Checkstyle against some source files.
+ */
 public class Checkstyle extends SourceTask implements VerificationTask {
     private File configFile;
 
diff --git a/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java b/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java
index c5e239e..f0bdc43 100644
--- a/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java
+++ b/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarc.java
@@ -20,6 +20,9 @@ import org.gradle.api.tasks.*;
 
 import java.io.File;
 
+/**
+ * Runs CodeNarc against some source files.
+ */
 public class CodeNarc extends SourceTask implements VerificationTask {
     private AntCodeNarc antCodeNarc = new AntCodeNarc();
 
diff --git a/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/package-info.java b/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/package-info.java
new file mode 100644
index 0000000..fa4278b
--- /dev/null
+++ b/subprojects/gradle-code-quality/src/main/groovy/org/gradle/api/plugins/quality/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A {@link org.gradle.api.Plugin} which measures and enforces code quality for Java and Groovy projects.
+ */
+package org.gradle.api.plugins.quality;
diff --git a/subprojects/gradle-core/core.gradle b/subprojects/gradle-core/core.gradle
index c2de68c..e55befa 100644
--- a/subprojects/gradle-core/core.gradle
+++ b/subprojects/gradle-core/core.gradle
@@ -18,18 +18,13 @@ import java.text.DateFormat
 import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
 
 apply plugin: 'groovy'
+apply from: "$rootDir/gradle/integTest.gradle"
 
 configurations {
     testFixtures
     testFixturesRuntime {
         extendsFrom testFixtures, testRuntime
     }
-    integTestCompile {
-        extendsFrom testCompile
-    }
-    integTestRuntime {
-        extendsFrom intTestCompile, testRuntime
-    }
     integTestFixtures {
         extendsFrom testFixtures
     }
@@ -38,13 +33,6 @@ configurations {
     }
 }
 
-sourceSets {
-    integTest {
-        compileClasspath = sourceSets.test.classes + sourceSets.main.classes + configurations.integTestCompile
-        runtimeClasspath = classes + compileClasspath + configurations.integTestRuntime
-    }
-}
-
 dependencies {
     groovy libraries.groovy_depends
 
@@ -52,8 +40,7 @@ dependencies {
 
     compile libraries.ivy, "com.jcraft:jsch:0.1.42 at jar", 'com.jcraft:jzlib:1.0.7 at jar'
 
-    compile libraries.jopt_simple,
-            libraries.ant,
+    compile libraries.ant,
             libraries.ant_nodeps,
             libraries.logback_classic,
             libraries.logback_core,
@@ -65,7 +52,7 @@ dependencies {
             libraries.google_collections,
             "commons-collections:commons-collections:3.2.1 at jar",
             "slide:webdavlib:2.0 at jar",
-            "org.apache.maven:maven-ant-tasks:2.1.0 at jar",
+            "org.apache.maven:maven-ant-tasks:2.1.1 at jar",
             libraries.asm_all,
             'org.fusesource.jansi:jansi:1.2.1',
             'org.jruby.ext.posix:jna-posix:1.0.3',
@@ -79,8 +66,7 @@ dependencies {
 
     testCompile libraries.xmlunit
 
-    runtime libraries.ant_launcher
-    testCompile libraries.ant_launcher
+    compile libraries.ant_launcher
 
     integTestCompile libraries.jetty_depends
 
@@ -102,26 +88,18 @@ ide.dependsOn ideVersionProperties
 
 ideaModule {
     dependsOn ideVersionProperties
-    testSourceDirs += project.sourceSets.integTest.groovy.srcDirs
     scopes.COMPILE.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files(new File(ideDir, "resources/test/")))))
-    scopes.TEST.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files(project.sourceSets.integTest.resources.srcDirs))))
-    scopes.TEST.plus.add(configurations.integTestCompile)
-    scopes.TEST.plus.add(configurations.integTestRuntime)
 }
 
 eclipseClasspath {
     dependsOn ideVersionProperties
     plusConfigurations.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files(new File(ideDir, "resources/test/")))))
     plusConfigurations.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files(project.sourceSets.integTest.resources.srcDirs))))
-    plusConfigurations.add(configurations.integTestCompile)
-    plusConfigurations.add(configurations.integTestRuntime)
 	whenConfigured { classpath ->
 		classpath.entries.removeAll { entry -> entry.kind == 'src' && entry.path.startsWith('src/integTest/resources')}
 	}
 }
 
-
-
 [compileGroovy, compileTestGroovy]*.groovyOptions*.fork(memoryInitialSize: '128M', memoryMaximumSize: '1G')
 
 test {
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
index 997d0d2..699b042 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/ArchiveIntegrationTest.groovy
@@ -19,6 +19,7 @@ package org.gradle.integtests
 
 import org.gradle.util.TestFile
 import org.junit.Test
+import static org.junit.Assert.*
 import static org.hamcrest.Matchers.*
 
 public class ArchiveIntegrationTest extends AbstractIntegrationTest {
@@ -555,7 +556,7 @@ public class ArchiveIntegrationTest extends AbstractIntegrationTest {
         )
     }
 
-    @Test public void canMergeArchivesIntoAnotherArchive() {
+    @Test public void canMergeArchivesIntoAnotherZip() {
         createZip('test.zip') {
             shared {
                 file 'zip.txt'
@@ -598,6 +599,47 @@ public class ArchiveIntegrationTest extends AbstractIntegrationTest {
         expandDir.assertHasDescendants('shared/zip.txt', 'zipdir1/file1.txt', 'shared/tar.txt', 'tardir1/file1.txt', 'shared/dir.txt', 'dir1/file1.txt')
     }
 
+    @Test public void usesManifestFromJarTaskWhenMergingJars() {
+        createDir('src1') {
+            dir1 { file 'file1.txt' }
+        }
+        createDir('src2') {
+            dir2 { file 'file2.txt' }
+        }
+        testFile('build.gradle') << '''
+        task jar1(type: Jar) {
+            from 'src1'
+            destinationDir = buildDir
+            archiveName = 'test1.zip'
+            manifest { attributes(attr: 'jar1') }
+        }
+        task jar2(type: Jar) {
+            from 'src2'
+            destinationDir = buildDir
+            archiveName = 'test2.zip'
+            manifest { attributes(attr: 'jar2') }
+        }
+        task jar(type: Jar) {
+            dependsOn jar1, jar2
+            from zipTree(jar1.archivePath), zipTree(jar2.archivePath)
+            manifest { attributes(attr: 'value') }
+            destinationDir = buildDir
+            archiveName = 'test.zip'
+        }
+        '''
+
+        inTestDirectory().withTasks('jar').run()
+        TestFile jar = testFile('build/test.zip')
+
+        def manifest = jar.manifest
+        println manifest.mainAttributes
+        assertThat(manifest.mainAttributes.getValue('attr'), equalTo('value'))
+
+        TestFile expandDir = testFile('expected')
+        jar.unzipTo(expandDir)
+        expandDir.assertHasDescendants('dir1/file1.txt', 'dir2/file2.txt', 'META-INF/MANIFEST.MF')
+    }
+
     private def createZip(String name, Closure cl) {
         TestFile zipRoot = testFile("${name}.root")
         TestFile zip = testFile(name)
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BackwardsCompatibilityIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BackwardsCompatibilityIntegrationTest.groovy
index 4ec4c6c..0bcbc39 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BackwardsCompatibilityIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BackwardsCompatibilityIntegrationTest.groovy
@@ -20,28 +20,24 @@ import org.junit.Test
 import org.gradle.integtests.fixtures.GradleDistributionExecuter
 import org.junit.Rule
 import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.AbstractGradleExecuter
-import org.gradle.integtests.fixtures.ExecutionResult
-import org.gradle.integtests.fixtures.ExecutionFailure
+
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.GradleExecuter
-import org.gradle.util.TestFile
-import org.gradle.integtests.fixtures.ForkingGradleExecuter
 
 @RunWith(DistributionIntegrationTestRunner.class)
 class BackwardsCompatibilityIntegrationTest {
     @Rule public final GradleDistribution dist = new GradleDistribution()
     @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
     @Rule public final TestResources resources = new TestResources()
-    private final GradleExecuter gradle08 = new PreviousGradleVersionExecuter(version: '0.8', dist: dist)
-    private final GradleExecuter gradle09preview3 = new PreviousGradleVersionExecuter(version: '0.9-preview-3', dist: dist)
+    private final GradleExecuter gradle08 = dist.previousVersion('0.8')
+    private final GradleExecuter gradle09rc1 = dist.previousVersion('0.9-rc-1')
 
     @Test
     public void canBuildJavaProject() {
         dist.testFile('buildSrc/src/main/groovy').assertIsDir()
 
         // Upgrade and downgrade
-        eachVersion([gradle08, gradle09preview3, executer, gradle09preview3, gradle08]) { version ->
+        eachVersion([gradle08, gradle09rc1, executer, gradle09rc1, gradle08]) { version ->
             version.inDirectory(dist.testDir).withTasks('build').run()
         }
     }
@@ -58,46 +54,3 @@ class BackwardsCompatibilityIntegrationTest {
     }
 }
 
-class PreviousGradleVersionExecuter extends AbstractGradleExecuter {
-    def GradleDistribution dist
-    def String version
-
-    def String toString() {
-        "Gradle $version"
-    }
-
-    protected ExecutionResult doRun() {
-        TestFile gradleHome = findGradleHome()
-
-        ForkingGradleExecuter executer = new ForkingGradleExecuter(gradleHome)
-        copyTo(executer)
-        return executer.run()
-    }
-
-    private TestFile findGradleHome() {
-        // maybe download and unzip distribution
-        TestFile versionsDir = dist.distributionsDir.parentFile.file('previousVersions')
-        TestFile gradleHome = versionsDir.file("gradle-$version")
-        TestFile markerFile = gradleHome.file('ok.txt')
-        if (!markerFile.isFile()) {
-            TestFile zipFile = dist.userHomeDir.parentFile.file("gradle-$version-bin.zip")
-            if (!zipFile.isFile()) {
-                try {
-                    URL url = new URL("http://dist.codehaus.org/gradle/${zipFile.name}")
-                    System.out.println("downloading $url");
-                    zipFile.copyFrom(url)
-                } catch (Throwable t) {
-                    zipFile.delete()
-                    throw t
-                }
-            }
-            zipFile.usingNativeTools().unzipTo(versionsDir)
-            markerFile.touch()
-        }
-        return gradleHome
-    }
-
-    protected ExecutionFailure doRunWithFailure() {
-        throw new UnsupportedOperationException();
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
index d3b223a..257066f 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/BuildScriptExecutionIntegrationTest.groovy
@@ -48,4 +48,34 @@ class BuildScriptExecutionIntegrationTest extends AbstractIntegrationTest {
         assertThat(result.error, containsString('error message'))
         assertThat(result.error, not(containsString('quiet message')))
     }
+
+    @Test
+    public void buildScriptCanContainATaskDefinition() {
+        testFile('build.gradle') << '''
+            task t(type: SomeTask)
+
+            class SomeTask extends DefaultTask {
+            }
+'''
+
+        inTestDirectory().withTaskList().run()
+    }
+
+    @Test
+    public void buildScriptCanContainOnlyClassDefinitions() {
+        testFile('build.gradle') << '''
+            class TestComparable implements Comparable<TestComparable>, SomeInterface {
+                int compareTo(TestComparable t) {
+                    return 0
+                }
+                void main() { }
+            }
+
+            interface SomeInterface {
+                void main()
+            }
+'''
+
+        inTestDirectory().withTaskList().run()
+    }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
index 996eb6a..5986a25 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
@@ -47,9 +47,4 @@ public class CommandLineIntegrationTest {
         }
         executer.inDirectory(javaprojectDir).withTasks('classes').run()
     }
-
-    @Test
-    public void canUseVersionCommandLineOption() {
-        executer.withArguments('-v').run()
-    }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy
index e3825ca..f0d8f42 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IdeaIntegrationTest.groovy
@@ -23,6 +23,10 @@ import org.gradle.integtests.fixtures.TestResources
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.gradle.util.TestFile
+import org.custommonkey.xmlunit.Diff
+import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier
+import org.custommonkey.xmlunit.XMLAssert
 
 @RunWith(DistributionIntegrationTestRunner)
 class IdeaIntegrationTest {
@@ -35,6 +39,52 @@ class IdeaIntegrationTest {
 
     @Test
     public void canCreateAndDeleteMetaData() {
-        executer.withTasks('idea', 'cleanIdea').run()
+        executer.withTasks('idea').run()
+
+        assertHasExpectedContents('root.ipr')
+        assertHasExpectedContents('root.iws')
+        assertHasExpectedContents('root.iml')
+        assertHasExpectedContents('api/api.iml')
+        assertHasExpectedContents('webservice/webservice.iml')
+
+        executer.withTasks('cleanIdea').run()
+    }
+
+    @Test
+    public void worksWithAnEmptyProject() {
+        executer.withTasks('idea').run()
+
+        assertHasExpectedContents('root.ipr')
+        assertHasExpectedContents('root.iml')
+    }
+
+    @Test
+    public void worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied() {
+        executer.withTasks('idea').run()
+
+        assertHasExpectedContents('root.ipr')
+    }
+
+    @Test
+    public void worksWithNonStandardLayout() {
+        executer.inDirectory(dist.testDir.file('root')).withTasks('idea').run()
+
+        assertHasExpectedContents('root/root.ipr')
+        assertHasExpectedContents('root/root.iml')
+        assertHasExpectedContents('top-level.iml')
+//        assertHasExpectedContents('a child project/a child.iml')
+    }
+
+    def assertHasExpectedContents(String path) {
+        TestFile file = dist.testDir.file(path).assertIsFile()
+        TestFile expectedFile = dist.testDir.file("expectedFiles/${path}.xml").assertIsFile()
+
+        def cache = dist.userHomeDir.file("cache")
+        def cachePath = cache.absolutePath.replace(File.separator, '/')
+        def expectedXml = expectedFile.text.replace('@CACHE_DIR@', cachePath)
+
+        Diff diff = new Diff(expectedXml, file.text)
+        diff.overrideElementQualifier(new ElementNameAndAttributeQualifier())
+        XMLAssert.assertXMLEqual(diff, true);
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy
index 44b7272..c82822c 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/IncrementalTestIntegrationTest.groovy
@@ -23,7 +23,7 @@ import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import static org.hamcrest.Matchers.*
-import org.gradle.integtests.testng.TestNgExecutionResult
+import org.gradle.integtests.testng.TestNGExecutionResult
 
 class IncrementalTestIntegrationTest {
     @Rule public final GradleDistribution distribution = new GradleDistribution()
@@ -82,7 +82,7 @@ class IncrementalTestIntegrationTest {
 
         executer.withTasks('test').run().assertTasksNotSkipped(':test')
 
-        result = new TestNgExecutionResult(distribution.testDir)
+        result = new TestNGExecutionResult(distribution.testDir)
         result.assertTestClassesExecuted('TestNGTest')
 
         executer.withTasks('test').run().assertTasksNotSkipped()
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
index b3758a7..c83226a 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/JUnitIntegrationTest.groovy
@@ -108,7 +108,7 @@ public class JUnitIntegrationTest {
         failure = executer.withTasks('test').withArguments('-Dtest.single=NotATest').runWithFailure()
         failure.assertHasCause('Could not find matching test for pattern: NotATest')
     }
-    
+
     @Test
     public void canUseTestSuperClassesFromAnotherProject() {
         TestFile testDir = dist.getTestDir();
@@ -180,7 +180,7 @@ public class JUnitIntegrationTest {
         result.assertTestClassesExecuted('org.gradle.EmptyRunWithSubclass', 'org.gradle.TestsOnInner$SomeInner')
         result.testClass('org.gradle.EmptyRunWithSubclass').assertTestsExecuted('ok')
         result.testClass('org.gradle.EmptyRunWithSubclass').assertTestPassed('ok')
-        result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')        
+        result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')
     }
 
     @Test
@@ -346,4 +346,14 @@ public class JUnitIntegrationTest {
         assertThat(result.getOutput(), containsLine("START [test testError(SomeTest)] [testError]"));
         assertThat(result.getOutput(), containsLine("FINISH [test testError(SomeTest)] [testError] [java.lang.RuntimeException: message]"));
     }
+
+    @Test
+    public void canHaveMultipleTestTaskInstances() {
+        executer.withTasks('check').run()
+
+        JUnitTestExecutionResult result = new JUnitTestExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.Test1', 'org.gradle.Test2')
+        result.testClass('org.gradle.Test1').assertTestPassed('ok')
+        result.testClass('org.gradle.Test2').assertTestPassed('ok')
+    }
 }
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
index b6b977a..168873f 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/LoggingIntegrationTest.groovy
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2010 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 
 package org.gradle.integtests
 
+import junit.framework.AssertionFailedError
 import org.gradle.integtests.fixtures.ExecutionResult
 import org.gradle.integtests.fixtures.GradleDistribution
 import org.gradle.integtests.fixtures.GradleDistributionExecuter
@@ -23,192 +24,322 @@ import org.gradle.util.TestFile
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import static org.gradle.util.Matchers.*
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
 
 /**
  * @author Hans Dockter
  */
-// todo To make this test stronger, we should check against the output of a file appender. Right now GradleLauncher does not provided this easily but eventually will.
 @RunWith(DistributionIntegrationTestRunner.class)
 class LoggingIntegrationTest {
     @Rule public final GradleDistribution dist = new GradleDistribution()
     @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources resources = new TestResources()
+    @Rule public final Sample sampleResources = new Sample()
 
-    List quietMessages = [
-            'An info log message which is always logged.',
-            'A message which is logged at QUIET level',
-            'A task message which is logged at QUIET level',
-            'quietProject2ScriptClassPathOut',
-            'quietProject2CallbackOut',
-            'settings quiet out',
-            'init QUIET out',
-            'init callback quiet out',
-            'buildSrc quiet',
-            'nestedBuild/buildSrc quiet',
-            'nestedBuild quiet',
-            'nestedBuild task quiet',
-            'external QUIET message'
-    ]
-    List errorMessages = [
-            'An error log message.',
-            'An error message which is logged at ERROR level',
-            'external ERROR error message',
-            '[ant:echo] An error message logged from Ant',
-            'A severe log message logged using JUL',
-            'init ERROR err'
-    ]
-    List warningMessages = [
-            'A warning log message.',
-            'A task error message which is logged at WARN level',
-            '[ant:echo] A warn message logged from Ant',
-            'A warning log message logged using JUL'
-    ]
-    List lifecycleMessages = [
-            'A lifecycle info log message.',
-            'An error message which is logged at LIFECYCLE level',
-            'A task message which is logged at LIFECYCLE level',
-            'settings lifecycle log',
-            'init lifecycle log',
-            'external LIFECYCLE error message',
-            'external LIFECYCLE log message',
-            'LOGGER: evaluating :',
-            'LOGGER: evaluating :project1',
-            'LOGGER: evaluating :project2',
-            'LOGGER: executing :project1:logInfo',
-            'LOGGER: executing :project1:logLifecycle',
-            'LOGGER: executing :project1:nestedBuildLog',
-            'LOGGER: executing :project1:log',
-            ':buildSrc:classes',
-            ':nestedBuild:log'
-    ]
-    List infoMessages = [
-            'An info log message.',
-            'A message which is logged at INFO level',
-            'A task message which is logged at INFO level',
-            '[ant:echo] An info message logged from Ant',
-            'An info log message logged using SLF4j',
-            'An info log message logged using JCL',
-            'An info log message logged using Log4j',
-            'An info log message logged using JUL',
-            'A config log message logged using JUL',
-            'infoProject2Out',
-            'infoProject2ScriptClassPathOut',
-            'settings info out',
-            'settings info log',
-            'init INFO out',
-            'init INFO err',
-            'init info log',
-            'LOGGER: build finished',
-            'LOGGER: evaluated project',
-            'LOGGER: executed task',
-            'LOGGER: task starting work',
-            'LOGGER: task completed work',
-            'buildSrc info',
-            'nestedBuild/buildSrc info',
-            'nestedBuild info',
-            'external INFO message'
-    ]
-    List debugMessages = [
-            'A debug log message.',
-            '[ant:echo] A debug message logged from Ant',
-            'A fine log message logged using JUL'
-    ]
-    List traceMessages = [
-            'A trace log message.'
-    ]
-    List forbiddenMessages = [
-            // the default message generated by JUL
-            'INFO: An info log message logged using JUL',
-            // the custom logger should override this
-            'BUILD SUCCESSFUL'
-    ]
-    List allOuts = [
-            errorMessages,
-            quietMessages,
-            warningMessages,
-            lifecycleMessages,
-            infoMessages,
-            debugMessages,
-            traceMessages,
-            forbiddenMessages
-    ]
+    private final LogOutput logOutput = new LogOutput() {{
+        quiet(
+                'An info log message which is always logged.',
+                'A message which is logged at QUIET level',
+                'Text which is logged at QUIET level',
+                'A task message which is logged at QUIET level',
+                'quietProject2ScriptClassPathOut',
+                'quietProject2CallbackOut',
+                'settings quiet out',
+                'init QUIET out',
+                'init callback quiet out',
+                'buildSrc quiet',
+                'nestedBuild/buildSrc quiet',
+                'nestedBuild quiet',
+                'nestedBuild task quiet',
+                'external QUIET message')
+        error(
+                'An error log message.',
+                'An error message which is logged at ERROR level',
+                'external ERROR error message',
+                '[ant:echo] An error message logged from Ant',
+                'A severe log message logged using JUL',
+                'init ERROR err'
+        )
+        warning(
+                'A warning log message.',
+                'A task error message which is logged at WARN level',
+                '[ant:echo] A warn message logged from Ant',
+                'A warning log message logged using JUL'
+        )
+        lifecycle(
+                'A lifecycle info log message.',
+                'An error message which is logged at LIFECYCLE level',
+                'A task message which is logged at LIFECYCLE level',
+                'settings lifecycle log',
+                'init lifecycle log',
+                'external LIFECYCLE error message',
+                'external LIFECYCLE log message',
+                'LOGGER: evaluating :',
+                'LOGGER: evaluating :project1',
+                'LOGGER: evaluating :project2',
+                'LOGGER: executing :project1:logInfo',
+                'LOGGER: executing :project1:logLifecycle',
+                'LOGGER: executing :project1:nestedBuildLog',
+                'LOGGER: executing :project1:log',
+                ':buildSrc:classes',
+                ':nestedBuild:log'
+        )
+        info(
+                'An info log message.',
+                'A message which is logged at INFO level',
+                'Text which is logged at INFO level',
+                'A task message which is logged at INFO level',
+                '[ant:echo] An info message logged from Ant',
+                'An info log message logged using SLF4j',
+                'An info log message logged using JCL',
+                'An info log message logged using Log4j',
+                'An info log message logged using JUL',
+                'A config log message logged using JUL',
+                'infoProject2Out',
+                'infoProject2ScriptClassPathOut',
+                'settings info out',
+                'settings info log',
+                'init INFO out',
+                'init INFO err',
+                'init info log',
+                'LOGGER: build finished',
+                'LOGGER: evaluated project',
+                'LOGGER: executed task',
+                'LOGGER: task starting work',
+                'LOGGER: task completed work',
+                'buildSrc info',
+                'nestedBuild/buildSrc info',
+                'nestedBuild info',
+                'external INFO message'
+        )
+        debug(
+                'A debug log message.',
+                '[ant:echo] A debug message logged from Ant',
+                'A fine log message logged using JUL'
+        )
+        trace(
+                'A trace log message.'
+        )
+        forbidden(
+                // the default message generated by JUL
+                'INFO: An info log message logged using JUL',
+                // the custom logger should override this
+                'BUILD SUCCESSFUL'
+        )
+    }}
 
-    LogLevel quiet = new LogLevel(
-            args: ['-q'],
-            includeMessages: [quietMessages, errorMessages]
-    )
-    LogLevel lifecycle = new LogLevel(
-            args: [],
-            includeMessages: [quietMessages, errorMessages, warningMessages, lifecycleMessages]
-    )
-    LogLevel info = new LogLevel(
-            args: ['-i'],
-            includeMessages: [quietMessages, errorMessages, warningMessages, lifecycleMessages, infoMessages]
-    )
-    LogLevel debug = new LogLevel(
-            args: ['-d'],
-            includeMessages: [quietMessages, errorMessages, warningMessages, lifecycleMessages, infoMessages, debugMessages],
-            matchPartialLine: true
-    )
+    private final LogOutput sample = new LogOutput() {{
+        error('An error log message.')
+        quiet('An info log message which is always logged.')
+        quiet('A message which is logged at QUIET level')
+        warning('A warning log message.')
+        lifecycle('A lifecycle info log message.')
+        info('An info log message.')
+        info('A message which is logged at INFO level')
+        info('A task message which is logged at INFO level')
+        info('An info log message logged using SLF4j')
+        debug('A debug log message.')
+        forbidden('A trace log message.')
+    }}
+
+    private final LogOutput multiThreaded = new LogOutput() {{
+        (1..10).each { thread ->
+            (1..100).each { iteration ->
+                lifecycle("log message from thread $thread iteration $iteration")
+                quiet("stdout message from thread $thread iteration $iteration")
+                quiet("styled text message from thread $thread iteration $iteration")
+            }
+        }
+    }}
 
     @Test
     public void quietLogging() {
-        checkOutput(quiet)
+        checkOutput(this.&run, logOutput.quiet)
     }
 
     @Test
     public void lifecycleLogging() {
-        checkOutput(lifecycle)
+        checkOutput(this.&run, logOutput.lifecycle)
     }
 
     @Test
     public void infoLogging() {
-        checkOutput(info)
+        checkOutput(this.&run, logOutput.info)
     }
 
     @Test
     public void debugLogging() {
-        checkOutput(debug)
+        checkOutput(this.&run, logOutput.debug)
+    }
+
+    @Test @UsesSample('userguide/tutorial/logging')
+    public void sampleQuietLogging() {
+        checkOutput(this.&runSample, sample.quiet)
+    }
+
+    @Test @UsesSample('userguide/tutorial/logging')
+    public void sampleLifecycleLogging() {
+        checkOutput(this.&runSample, sample.lifecycle)
+    }
+
+    @Test @UsesSample('userguide/tutorial/logging')
+    public void sampleInfoLogging() {
+        checkOutput(this.&runSample, sample.info)
+    }
+
+    @Test @UsesSample('userguide/tutorial/logging')
+    public void sampleDebugLogging() {
+        checkOutput(this.&runSample, sample.debug)
+    }
+
+    @Test
+    public void multiThreadedQuietLogging() {
+        checkOutput(this.&runMultiThreaded, multiThreaded.quiet)
+    }
+
+    @Test
+    public void multiThreadedlifecycleLogging() {
+        checkOutput(this.&runMultiThreaded, multiThreaded.lifecycle)
+    }
+
+    @Test
+    public void multiThreadedDebugLogging() {
+        checkOutput(this.&runMultiThreaded, multiThreaded.debug)
     }
 
-    void checkOutput(LogLevel level) {
-        TestFile loggingDir = dist.samplesDir.file('logging')
+    def run(LogLevel level) {
+        resources.maybeCopy('LoggingIntegrationTest/logging')
+        TestFile loggingDir = dist.testDir
         loggingDir.file("buildSrc/build/.gradle").deleteDir()
         loggingDir.file("nestedBuild/buildSrc/.gradle").deleteDir()
 
         String initScript = new File(loggingDir, 'init.gradle').absolutePath
         String[] allArgs = level.args + ['-I', initScript]
+        return executer.inDirectory(loggingDir).withArguments(allArgs).withTasks('log').run()
+    }
 
-        ExecutionResult result = executer.inDirectory(loggingDir).withArguments(allArgs).withTasks('log').run()
-        level.includeMessages.each {List messages ->
-            if (messages == errorMessages) {
-                checkOuts(true, result.error, messages, level.matchPartialLine)
-            }
-            else {
-                checkOuts(true, result.output, messages, level.matchPartialLine)
-            }
+    def runMultiThreaded(LogLevel level) {
+        resources.maybeCopy('LoggingIntegrationTest/multiThreaded')
+        return executer.withArguments(level.args).withTasks('log').run()
+    }
+    
+    def runSample(LogLevel level) {
+        return executer.inDirectory(sampleResources.dir).withArguments(level.args).withTasks('log').run()
+    }
+
+    void checkOutput(Closure run, LogLevel level) {
+        ExecutionResult result = run.call(level)
+        level.checkOuts(result)
+    }
+}
+
+class LogLevel {
+    List args
+    List infoMessages
+    List errorMessages
+    List allMessages
+    Closure matchPartialLine = {expected, actual -> expected == actual }
+
+    def getForbiddenMessages() {
+        allMessages - (infoMessages + errorMessages)
+    }
+
+    def checkOuts(ExecutionResult result) {
+        infoMessages.each {List messages ->
+            checkOuts(true, result.output, messages, matchPartialLine)
+        }
+        errorMessages.each {List messages ->
+            checkOuts(true, result.error, messages, matchPartialLine)
         }
-        (allOuts- level.includeMessages).each {List messages ->
-            checkOuts(false, result.output, messages, true)
-            checkOuts(false, result.error, messages, true)
+        forbiddenMessages.each {List messages ->
+            checkOuts(false, result.output, messages) {expected, actual-> actual.contains(expected)}
+            checkOuts(false, result.error, messages) {expected, actual-> actual.contains(expected)}
         }
     }
 
-    void checkOuts(boolean shouldContain, String result, List outs, boolean partialLine) {
+    def checkOuts(boolean shouldContain, String result, List outs, Closure partialLine) {
         outs.each {String expectedOut ->
-            def matcher = partialLine ? containsLine(containsString(expectedOut)) : containsLine(expectedOut)
-            if (!shouldContain) {
-                matcher = not(matcher)
+            boolean found = result.readLines().find {partialLine.call(expectedOut, it)}
+            if (!found && shouldContain) {
+                throw new AssertionFailedError("Could not find expected line '$expectedOut' in output:\n$result")
+            }
+            if (found && !shouldContain) {
+                throw new AssertionFailedError("Found unexpected line '$expectedOut' in output:\n$result")
             }
-            assertThat(result, matcher)
         }
     }
 }
 
-class LogLevel {
-    List args
-    List includeMessages
-    boolean matchPartialLine
+class LogOutput {
+    final List quietMessages = []
+    final List errorMessages = []
+    final List warningMessages = []
+    final List lifecycleMessages = []
+    final List infoMessages = []
+    final List debugMessages = []
+    final List traceMessages = []
+    final List forbiddenMessages = []
+    final List allOuts = [
+            errorMessages,
+            quietMessages,
+            warningMessages,
+            lifecycleMessages,
+            infoMessages,
+            debugMessages,
+            traceMessages,
+            forbiddenMessages
+    ]
+
+    def quiet(String... msgs) {
+        quietMessages.addAll(msgs)
+    }
+    def error(String... msgs) {
+        errorMessages.addAll(msgs)
+    }
+    def warning(String... msgs) {
+        warningMessages.addAll(msgs)
+    }
+    def lifecycle(String... msgs) {
+        warningMessages.addAll(msgs)
+    }
+    def info(String... msgs) {
+        infoMessages.addAll(msgs)
+    }
+    def debug(String... msgs) {
+        debugMessages.addAll(msgs)
+    }
+    def trace(String... msgs) {
+        traceMessages.addAll(msgs)
+    }
+    def forbidden(String... msgs) {
+        forbiddenMessages.addAll(msgs)
+    }
+
+    final LogLevel quiet = new LogLevel(
+            args: ['-q'],
+            infoMessages: [quietMessages],
+            errorMessages: [errorMessages],
+            allMessages: allOuts
+    )
+    final LogLevel lifecycle = new LogLevel(
+            args: [],
+            infoMessages: [quietMessages, warningMessages, lifecycleMessages],
+            errorMessages: [errorMessages],
+            allMessages: allOuts
+    )
+    final LogLevel info = new LogLevel(
+            args: ['-i'],
+            infoMessages: [quietMessages, warningMessages, lifecycleMessages, infoMessages],
+            errorMessages: [errorMessages],
+            allMessages: allOuts
+    )
+    final LogLevel debug = new LogLevel(
+            args: ['-d'],
+            infoMessages: [quietMessages, warningMessages, lifecycleMessages, infoMessages, debugMessages],
+            errorMessages: [errorMessages],
+            allMessages: allOuts,
+            matchPartialLine: {expected, actual -> actual.endsWith(expected) /*&& actual =~ /\[.+?\] \[.+?\] .+/ */}
+    )
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy
index e60ea9a..489b1c8 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/SamplesJavaMultiProjectIntegrationTest.groovy
@@ -198,7 +198,7 @@ class SamplesJavaMultiProjectIntegrationTest {
     @Test
     public void additionalProjectDependenciesTasks() {
         TestFile apiDir = javaprojectDir.file(API_NAME)
-        executer.inDirectory(apiDir).withTasks('classes').withArguments("-A javadoc").run()
+        executer.inDirectory(apiDir).withTasks('classes').withArguments("-Ajavadoc").run()
         assertExists(javaprojectDir, SHARED_NAME, 'build/docs/javadoc/index.html')
     }
 
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
index 939ab69..8c34704 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/UserGuideSamplesRunner.groovy
@@ -94,7 +94,7 @@ class UserGuideSamplesRunner extends Runner {
             if (executer instanceof GradleDistributionExecuter) {
                 ((GradleDistributionExecuter) executer).setInProcessStartParameterModifier(createModifier(rootProjectDir))
             }
-            
+
             ExecutionResult result = run.expectFailure ? executer.runWithFailure() : executer.run()
             if (run.outputFile) {
                 String expectedResult = replaceWithPlatformNewLines(dist.userGuideOutputDir.file(run.outputFile).text)
@@ -121,7 +121,7 @@ class UserGuideSamplesRunner extends Runner {
         for (; pos < actualLines.size() && pos < expectedLines.size(); pos++) {
             String expectedLine = expectedLines[pos]
             String actualLine = actualLines[pos]
-            boolean matches = actualLine == expectedLine
+            boolean matches = compare(expectedLine, actualLine)
             if (!matches) {
                 if (expectedLine.contains(actualLine)) {
                     Assert.fail("Missing text at line ${pos + 1}.${NL}Expected: ${expectedLine}${NL}Actual: ${actualLine}${NL}---${NL}Actual output:${NL}$actual${NL}---")
@@ -148,23 +148,34 @@ class UserGuideSamplesRunner extends Runner {
 
     List<String> normaliseOutput(List<String> lines) {
         lines.inject(new ArrayList<String>()) { List values, String line ->
-            if (line.matches('Total time: .+ secs')) {
-                values << 'Total time: 1 secs'
-            } else if (line.matches('Download .+')) {
+            if (line.matches('Download .+')) {
                 // ignore
             } else {
-                // Normalise default object toString() values
-                line = line.replaceAll('(\\w+(\\.\\w+)*)@\\p{XDigit}+', '$1 at 12345')
-                // Normalise $samplesDir
-                line = line.replaceAll(java.util.regex.Pattern.quote(dist.samplesDir.absolutePath), '/home/user/gradle/samples')
-                // Normalise file separators
-                line = line.replaceAll(java.util.regex.Pattern.quote(File.separator), '/')
                 values << line
             }
             values
         }
     }
 
+    boolean compare(String expected, String actual) {
+        if (actual == expected) {
+            return true
+        }
+
+        if (expected == 'Total time: 1 secs') {
+            return actual.matches('Total time: .+ secs')
+        }
+        
+        // Normalise default object toString() values
+        actual = actual.replaceAll('(\\w+(\\.\\w+)*)@\\p{XDigit}+', '$1 at 12345')
+        // Normalise $samplesDir
+        actual = actual.replaceAll(java.util.regex.Pattern.quote(dist.samplesDir.absolutePath), '/home/user/gradle/samples')
+        // Normalise file separators
+        actual = actual.replaceAll(java.util.regex.Pattern.quote(File.separator), '/')
+
+        return actual == expected
+    }
+
     static GradleDistributionExecuter.StartParameterModifier createModifier(File rootProjectDir) {
         {StartParameter parameter ->
             if (parameter.getCurrentDir() != null) {
@@ -222,9 +233,9 @@ class UserGuideSamplesRunner extends Runner {
         samplesByDir.values().collect {List<GradleRun> dirSamples ->
             Collection<GradleRun> runs = dirSamples.findAll {it.args}
             if (!runs) {
-                // No samples in this dir have any args, so just run gradle -t in the dir
+                // No samples in this dir have any args, so just run gradle tasks in the dir
                 def sample = dirSamples[0]
-                sample.args = ['-t']
+                sample.args = ['tasks']
                 sample
             } else {
                 return runs
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java
index 59a7629..086ff72 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/WorkerProcessIntegrationTest.java
@@ -190,7 +190,7 @@ public class WorkerProcessIntegrationTest {
         }
 
         public void start() {
-            WorkerProcessBuilder builder = workerFactory.newProcess();
+            WorkerProcessBuilder builder = workerFactory.create();
             builder.applicationClasspath(classPathRegistry.getClassPathFiles("ANT"));
             builder.sharedPackages("org.apache.tools.ant");
             builder.getJavaCommand().systemProperty("test.system.property", "value");
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/AbstractGradleExecuter.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/AbstractGradleExecuter.java
index 811ed62..a2d8d88 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/AbstractGradleExecuter.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/AbstractGradleExecuter.java
@@ -155,7 +155,7 @@ public abstract class AbstractGradleExecuter implements GradleExecuter {
             allArgs.add("--quiet");
         }
         if (taskList) {
-            allArgs.add("--tasks");
+            allArgs.add("tasks");
         }
         if (!searchUpwards) {
             allArgs.add("--no-search-upward");
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/BasicGradleDistribution.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/BasicGradleDistribution.java
new file mode 100644
index 0000000..4c8646f
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/BasicGradleDistribution.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.fixtures;
+
+import org.gradle.util.TestFile;
+
+public interface BasicGradleDistribution {
+    TestFile getGradleHomeDir();
+
+    String getVersion();
+}
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java
index 9ed774a..a6ebc8d 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ExecutionFailure.java
@@ -18,15 +18,15 @@ package org.gradle.integtests.fixtures;
 import org.hamcrest.Matcher;
 
 public interface ExecutionFailure extends ExecutionResult {
-    void assertHasLineNumber(int lineNumber);
+    ExecutionFailure assertHasLineNumber(int lineNumber);
 
-    void assertHasFileName(String filename);
+    ExecutionFailure assertHasFileName(String filename);
 
-    void assertHasCause(String description);
+    ExecutionFailure assertHasCause(String description);
 
-    void assertThatCause(Matcher<String> matcher);
+    ExecutionFailure assertThatCause(Matcher<String> matcher);
 
-    void assertHasDescription(String context);
+    ExecutionFailure assertHasDescription(String context);
 
-    void assertThatDescription(Matcher<String> matcher);
+    ExecutionFailure assertThatDescription(Matcher<String> matcher);
 }
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java
index d586400..cfd036b 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/ForkingGradleExecuter.java
@@ -196,19 +196,22 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
             super(result);
         }
 
-        public void assertHasLineNumber(int lineNumber) {
+        public ExecutionFailure assertHasLineNumber(int lineNumber) {
             assertThat(getError(), containsString(String.format(" line: %d", lineNumber)));
+            return this;
         }
 
-        public void assertHasFileName(String filename) {
+        public ExecutionFailure assertHasFileName(String filename) {
             assertThat(getError(), containsLine(startsWith(filename)));
+            return this;
         }
 
-        public void assertHasCause(String description) {
+        public ExecutionFailure assertHasCause(String description) {
             assertThatCause(startsWith(description));
+            return this;
         }
 
-        public void assertThatCause(final Matcher<String> matcher) {
+        public ExecutionFailure assertThatCause(final Matcher<String> matcher) {
             assertThat(getError(), containsLine(new BaseMatcher<String>() {
                 public boolean matches(Object o) {
                     String str = (String) o;
@@ -220,14 +223,17 @@ public class ForkingGradleExecuter extends AbstractGradleExecuter {
                     matcher.describeTo(description);
                 }
             }));
+            return this;
         }
 
-        public void assertHasDescription(String context) {
+        public ExecutionFailure assertHasDescription(String context) {
             assertThatDescription(startsWith(context));
+            return this;
         }
 
-        public void assertThatDescription(Matcher<String> matcher) {
+        public ExecutionFailure assertThatDescription(Matcher<String> matcher) {
             assertThat(getError(), containsLine(matcher));
+            return this;
         }
     }
 }
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java
index a08b95d..cf3180d 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistribution.java
@@ -16,6 +16,7 @@
 
 package org.gradle.integtests.fixtures;
 
+import org.gradle.util.GradleVersion;
 import org.gradle.util.TemporaryFolder;
 import org.gradle.util.TestFile;
 import org.gradle.util.TestFileContext;
@@ -28,7 +29,7 @@ import java.io.File;
 /**
  * Provides access to a Gradle distribution for integration testing.
  */
-public class GradleDistribution implements MethodRule, TestFileContext {
+public class GradleDistribution implements MethodRule, TestFileContext, BasicGradleDistribution {
     private static final TestFile USER_HOME_DIR;
     private static final TestFile GRADLE_HOME_DIR;
     private static final TestFile SAMPLES_DIR;
@@ -85,6 +86,10 @@ public class GradleDistribution implements MethodRule, TestFileContext {
         return GRADLE_HOME_DIR;
     }
 
+    public String getVersion() {
+        return new GradleVersion().getVersion();
+    }
+
     /**
      * The samples from the distribution. These are usually shared with other tests.
      */
@@ -129,6 +134,16 @@ public class GradleDistribution implements MethodRule, TestFileContext {
     }
 
     /**
+     * Returns an executer which can execute a previous version of Gradle.
+     *
+     * @param version The Gradle version
+     * @return An executer
+     */
+    public PreviousGradleVersionExecuter previousVersion(String version) {
+        return new PreviousGradleVersionExecuter(this, version);
+    }
+
+    /**
      * Returns a scratch-pad file for the current test. Equivalent to getTestDir().file(path)
      */
     public TestFile file(Object... path) {
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java
index 74842fa..c8077e2 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/GradleDistributionExecuter.java
@@ -33,7 +33,7 @@ import java.io.File;
  * executing Gradle in the current process.
  */
 public class GradleDistributionExecuter extends AbstractGradleExecuter implements MethodRule {
-    private static final String NOFORK_SYS_PROP = "org.gradle.integtest.nofork";
+    private static final String FORK_SYS_PROP = "org.gradle.integtest.fork";
     private static final boolean FORK;
     private GradleDistribution dist;
     private StartParameterModifier inProcessStartParameterModifier;
@@ -41,7 +41,7 @@ public class GradleDistributionExecuter extends AbstractGradleExecuter implement
     private boolean userHomeSet;
 
     static {
-        FORK = System.getProperty(NOFORK_SYS_PROP, "false").equalsIgnoreCase("false");
+        FORK = !System.getProperty(FORK_SYS_PROP, "true").equalsIgnoreCase("false");
     }
 
     public GradleDistributionExecuter(GradleDistribution dist) {
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java
index 9304f06..22bd733 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/InProcessGradleExecuter.java
@@ -17,7 +17,6 @@
 package org.gradle.integtests.fixtures;
 
 import junit.framework.AssertionFailedError;
-import org.gradle.BuildLogger;
 import org.gradle.BuildResult;
 import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
@@ -28,14 +27,10 @@ import org.gradle.api.execution.TaskExecutionGraph;
 import org.gradle.api.execution.TaskExecutionGraphListener;
 import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.Logging;
 import org.gradle.api.logging.StandardOutputListener;
 import org.gradle.api.tasks.TaskState;
-import org.gradle.execution.BuiltInTaskBuildExecuter;
-import org.gradle.execution.DependencyReportBuildExecuter;
-import org.gradle.execution.TaskReportBuildExecuter;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
-import org.gradle.util.Clock;
+import org.gradle.initialization.CommandLineParser;
+import org.gradle.initialization.DefaultCommandLineConverter;
 import org.hamcrest.Matcher;
 
 import java.io.File;
@@ -44,7 +39,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import static org.gradle.util.Matchers.*;
+import static org.gradle.util.Matchers.containsLine;
+import static org.gradle.util.Matchers.hasMessage;
+import static org.gradle.util.WrapUtil.toList;
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
@@ -86,13 +83,13 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
 
     @Override
     public InProcessGradleExecuter withTaskList() {
-        parameter.setBuildExecuter(new TaskReportBuildExecuter(BuiltInTaskBuildExecuter.ALL_PROJECTS_WILDCARD, true));
+        parameter.setTaskNames(toList("tasks"));
         return this;
     }
 
     @Override
     public InProcessGradleExecuter withDependencyList() {
-        parameter.setBuildExecuter(new DependencyReportBuildExecuter(BuiltInTaskBuildExecuter.ALL_PROJECTS_WILDCARD));
+        parameter.setTaskNames(toList("dependencies"));
         return this;
     }
 
@@ -116,7 +113,10 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
 
     @Override
     public GradleExecuter withArguments(List<String> args) {
-        new DefaultCommandLine2StartParameterConverter().convert(args.toArray(new String[args.size()]), parameter);
+        CommandLineParser parser = new CommandLineParser();
+        DefaultCommandLineConverter converter = new DefaultCommandLineConverter();
+        converter.configure(parser);
+        converter.convert(parser.parse(args), parameter);
         return this;
     }
 
@@ -151,7 +151,7 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
     }
 
-    private BuildResult doRun(OutputListenerImpl outputListener, OutputListenerImpl errorListener,
+    private BuildResult doRun(final OutputListenerImpl outputListener, OutputListenerImpl errorListener,
                               BuildListenerImpl listener) {
         assertCanExecute();
         if (isQuiet()) {
@@ -159,8 +159,6 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
         GradleLauncher gradleLauncher = GradleLauncher.newInstance(parameter);
         gradleLauncher.addListener(listener);
-        gradleLauncher.useLogger(new BuildLogger(Logging.getLogger(InProcessGradleExecuter.class), new Clock(),
-                parameter));
         gradleLauncher.addStandardOutputListener(outputListener);
         gradleLauncher.addStandardErrorListener(errorListener);
         try {
@@ -173,9 +171,6 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
     public void assertCanExecute() {
         assertNull(getExecutable());
         assertTrue(getEnvironmentVars().isEmpty());
-        assertFalse(parameter.isShowHelp());
-        assertFalse(parameter.isShowVersion());
-        assertFalse(parameter.isLaunchGUI());
     }
 
     public boolean canExecute() {
@@ -186,7 +181,8 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
         return true;
     }
-    private class BuildListenerImpl implements TaskExecutionGraphListener {
+
+    private static class BuildListenerImpl implements TaskExecutionGraphListener {
         private final List<String> executedTasks = new ArrayList<String>();
         private final List<String> skippedTasks = new ArrayList<String>();
 
@@ -196,7 +192,7 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
     }
 
-    private class OutputListenerImpl implements StandardOutputListener {
+    private static class OutputListenerImpl implements StandardOutputListener {
         private StringWriter writer = new StringWriter();
 
         @Override
@@ -209,7 +205,7 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
         }
     }
 
-    private class TaskListenerImpl implements TaskExecutionListener {
+    private static class TaskListenerImpl implements TaskExecutionListener {
         private final List<Task> planned;
         private final List<String> executedTasks;
         private final List<String> skippedTasks;
@@ -289,19 +285,23 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
             this.failure = failure;
         }
 
-        public void assertHasLineNumber(int lineNumber) {
+        public ExecutionFailure assertHasLineNumber(int lineNumber) {
             assertThat(failure.getMessage(), containsString(String.format(" line: %d", lineNumber)));
+            return this;
+
         }
 
-        public void assertHasFileName(String filename) {
+        public ExecutionFailure assertHasFileName(String filename) {
             assertThat(failure.getMessage(), startsWith(String.format("%s", filename)));
+            return this;
         }
 
-        public void assertHasCause(String description) {
+        public ExecutionFailure assertHasCause(String description) {
             assertThatCause(startsWith(description));
+            return this;
         }
 
-        public void assertThatCause(final Matcher<String> matcher) {
+        public ExecutionFailure assertThatCause(final Matcher<String> matcher) {
             if (failure instanceof LocationAwareException) {
                 LocationAwareException exception = (LocationAwareException) failure;
                 assertThat(exception.getReportableCauses(), hasItem(hasMessage(matcher)));
@@ -309,14 +309,17 @@ public class InProcessGradleExecuter extends AbstractGradleExecuter {
                 assertThat(failure.getCause(), notNullValue());
                 assertThat(failure.getCause().getMessage(), matcher);
             }
+            return this;
         }
 
-        public void assertHasDescription(String context) {
+        public ExecutionFailure assertHasDescription(String context) {
             assertThatDescription(startsWith(context));
+            return this;
         }
 
-        public void assertThatDescription(Matcher<String> matcher) {
+        public ExecutionFailure assertThatDescription(Matcher<String> matcher) {
             assertThat(failure.getMessage(), containsLine(matcher));
+            return this;
         }
     }
 }
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy
new file mode 100644
index 0000000..2b4982b
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/PreviousGradleVersionExecuter.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.fixtures
+
+import org.gradle.util.TestFile
+
+public class PreviousGradleVersionExecuter extends AbstractGradleExecuter implements BasicGradleDistribution {
+    private final GradleDistribution dist
+    def final String version
+
+    PreviousGradleVersionExecuter(GradleDistribution dist, String version) {
+        this.dist = dist
+        this.version = version
+    }
+
+    def String toString() {
+        "Gradle $version"
+    }
+
+    protected ExecutionResult doRun() {
+        ForkingGradleExecuter executer = new ForkingGradleExecuter(gradleHomeDir)
+        copyTo(executer)
+        return executer.run()
+    }
+
+    def TestFile getGradleHomeDir() {
+        return findGradleHome()
+    }
+
+    private TestFile findGradleHome() {
+        // maybe download and unzip distribution
+        TestFile versionsDir = dist.distributionsDir.parentFile.file('previousVersions')
+        TestFile gradleHome = versionsDir.file("gradle-$version")
+        TestFile markerFile = gradleHome.file('ok.txt')
+        if (!markerFile.isFile()) {
+            TestFile zipFile = dist.userHomeDir.parentFile.file("gradle-$version-bin.zip")
+            if (!zipFile.isFile()) {
+                try {
+                    URL url = new URL("http://dist.codehaus.org/gradle/${zipFile.name}")
+                    System.out.println("downloading $url");
+                    zipFile.copyFrom(url)
+                } catch (Throwable t) {
+                    zipFile.delete()
+                    throw t
+                }
+            }
+            zipFile.usingNativeTools().unzipTo(versionsDir)
+            markerFile.touch()
+        }
+        return gradleHome
+    }
+
+    protected ExecutionFailure doRunWithFailure() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/Sample.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/Sample.java
index f7cbf79..f806d46 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/Sample.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/Sample.java
@@ -20,29 +20,59 @@ import org.gradle.util.TestFile;
 import org.junit.rules.MethodRule;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+/**
+ * A Junit rule which copies a sample into the test directory before the test executes. Looks for a
+ * {@link org.gradle.integtests.fixtures.UsesSample} annotation on the test method to determine which sample the
+ * test requires. If not found, uses the default sample provided in the constructor.
+ */
 public class Sample implements MethodRule {
-    private final String name;
+    private final Logger logger = LoggerFactory.getLogger(Sample.class);
+    private final String defaultSampleName;
     private GradleDistribution dist;
     private TestFile sampleDir;
 
-    public Sample(String name) {
-        this.name = name;
+    public Sample(String defaultSampleName) {
+        this.defaultSampleName = defaultSampleName;
+    }
+
+    public Sample() {
+        this.defaultSampleName = null;
     }
 
     public Statement apply(final Statement base, FrameworkMethod method, Object target) {
         dist = RuleHelper.getField(target, GradleDistribution.class);
-        sampleDir = dist.getTestDir().file(name);
+        final String sampleName = getSampleName(method);
+        sampleDir = sampleName == null ? null : dist.getTestDir().file(sampleName);
+
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                TestFile srcDir = dist.getSamplesDir().file(name).assertIsDir();
-                srcDir.copyTo(sampleDir);
+                if (sampleName != null) {
+                    TestFile srcDir = dist.getSamplesDir().file(sampleName).assertIsDir();
+                    logger.debug("Copying sample '{}' to test directory.", sampleName);
+                    srcDir.copyTo(sampleDir);
+                } else {
+                    logger.debug("No sample specified for this test, skipping.");
+                }
                 base.evaluate();
             }
         };
     }
 
+    private String getSampleName(FrameworkMethod method) {
+        String sampleName;
+        UsesSample annotation = method.getAnnotation(UsesSample.class);
+        if (annotation == null) {
+            sampleName = defaultSampleName;
+        } else {
+            sampleName = annotation.value();
+        }
+        return sampleName;
+    }
+
     public TestFile getDir() {
         return sampleDir;
     }
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/TestResources.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/TestResources.java
index 750b83c..04b498c 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/TestResources.java
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/TestResources.java
@@ -22,6 +22,8 @@ import org.gradle.util.TestFile;
 import org.junit.rules.MethodRule;
 import org.junit.runners.model.FrameworkMethod;
 import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -33,6 +35,7 @@ import java.util.Collection;
  * Copies the contents of each such directory into a temporary directory for the test to use.
  */
 public class TestResources implements MethodRule {
+    private final Logger logger = LoggerFactory.getLogger(TestResources.class);
     private TemporaryFolder temporaryFolder;
     private final Collection<String> extraResources;
     private final Resources resources = new Resources();
@@ -76,10 +79,16 @@ public class TestResources implements MethodRule {
                 target.getClass().getSimpleName()));
     }
 
-    private void maybeCopy(String resource) {
+    /**
+     * Copies the given resource to the test directory.
+     */
+    public void maybeCopy(String resource) {
         TestFile dir = resources.findResource(resource);
         if (dir != null) {
+            logger.debug("Copying test resource '{}' from {} to test directory.", resource, dir);
             dir.copyTo(getDir());
+        } else {
+            logger.debug("Test resource '{}' not found, skipping.", resource);
         }
     }
 }
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/UsesSample.java b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/UsesSample.java
new file mode 100644
index 0000000..b501fb3
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/fixtures/UsesSample.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.fixtures;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+public @interface UsesSample {
+    String value();
+}
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy
new file mode 100644
index 0000000..aae10a7
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/SampleTestNGIntegrationTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.testng
+
+import org.gradle.integtests.DistributionIntegrationTestRunner
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * @author Tom Eyckmans
+ */
+ at RunWith(DistributionIntegrationTestRunner.class)
+public class SampleTestNGIntegrationTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample()
+
+    @Test @UsesSample('testng/suitexmlbuilder')
+    public void suiteXmlBuilder() {
+        executer.inDirectory(sample.dir).withTasks('clean', 'test').run()
+
+        def result = new TestNGExecutionResult(sample.dir)
+        result.assertTestClassesExecuted('org.gradle.testng.UserImplTest')
+        result.testClass('org.gradle.testng.UserImplTest').assertTestsExecuted('testOkFirstName')
+        result.testClass('org.gradle.testng.UserImplTest').assertTestPassed('testOkFirstName')
+    }
+
+    @Test @UsesSample('testng/java-jdk14-passing')
+    public void javaJdk14Passing() {
+        executer.inDirectory(sample.dir).withTasks('clean', 'test').run()
+
+        def result = new TestNGExecutionResult(sample.dir)
+        result.assertTestClassesExecuted('org.gradle.OkTest')
+        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
+    }
+    
+    @Test @UsesSample('testng/java-jdk15-passing')
+    public void javaJdk15Passing() {
+        executer.inDirectory(sample.dir).withTasks('clean', 'test').run()
+
+        def result = new TestNGExecutionResult(sample.dir)
+        result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.ConcreteTest', 'org.gradle.SuiteSetup', 'org.gradle.SuiteCleanup', 'org.gradle.TestSetup', 'org.gradle.TestCleanup')
+        result.testClass('org.gradle.OkTest').assertTestsExecuted('passingTest', 'expectedFailTest')
+        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
+        result.testClass('org.gradle.OkTest').assertTestPassed('expectedFailTest')
+        result.testClass('org.gradle.ConcreteTest').assertTestsExecuted('ok', 'alsoOk')
+        result.testClass('org.gradle.ConcreteTest').assertTestPassed('ok')
+        result.testClass('org.gradle.ConcreteTest').assertTestPassed('alsoOk')
+        result.testClass('org.gradle.SuiteSetup').assertConfigMethodPassed('setupSuite')
+        result.testClass('org.gradle.SuiteCleanup').assertConfigMethodPassed('cleanupSuite')
+        result.testClass('org.gradle.TestSetup').assertConfigMethodPassed('setupTest')
+        result.testClass('org.gradle.TestCleanup').assertConfigMethodPassed('cleanupTest')
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy
new file mode 100644
index 0000000..efc08c5
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGExecutionResult.groovy
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.gradle.integtests.testng
+
+import groovy.util.slurpersupport.GPathResult
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.util.TestFile
+import org.hamcrest.Matcher
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+
+class TestNGExecutionResult implements TestExecutionResult {
+    private final TestFile projectDir
+    private final GPathResult resultsXml
+
+    def TestNGExecutionResult(projectDir) {
+        this.projectDir = projectDir;
+        resultsXml = new XmlSlurper().parse(projectDir.file('build/reports/tests/testng-results.xml').assertIsFile())
+    }
+
+    TestExecutionResult assertTestClassesExecuted(String... testClasses) {
+        projectDir.file('build/reports/tests/index.html').assertIsFile()
+        def actualTestClasses = findTestClasses().keySet()
+        assertThat(actualTestClasses, equalTo(testClasses as Set))
+        this
+    }
+
+    TestClassExecutionResult testClass(String testClass) {
+        return new TestNgTestClassExecutionResult(testClass, findTestClass(testClass))
+    }
+
+    private def findTestClass(String testClass) {
+        def testClasses = findTestClasses()
+        if (!testClasses.containsKey(testClass)) {
+            fail("Could not find test class ${testClass}. Found ${testClasses.keySet()}")
+        }
+        testClasses[testClass]
+    }
+
+    private def findTestClasses() {
+        Map testClasses = [:]
+        resultsXml.suite.test.'class'.each {
+            testClasses.put(it. at name as String, it)
+        }
+        testClasses
+    }
+}
+
+private class TestNgTestClassExecutionResult implements TestClassExecutionResult {
+    def String testClass
+    def GPathResult testClassNode
+
+    def TestNgTestClassExecutionResult(String testClass, GPathResult resultXml) {
+        this.testClass = testClass
+        this.testClassNode = resultXml
+    }
+
+    TestClassExecutionResult assertTestsExecuted(String... testNames) {
+        def actualTestMethods = findTestMethods().keySet()
+        assertThat(actualTestMethods, equalTo(testNames as Set))
+        this
+    }
+
+    TestClassExecutionResult assertTestPassed(String name) {
+        def testMethodNode = findTestMethod(name)
+        assertEquals('PASS', testMethodNode. at status as String)
+        this
+    }
+
+    TestClassExecutionResult assertTestSkipped(String name) {
+        def testMethodNode = findTestMethod(name)
+        assertEquals('SKIP', testMethodNode. at status as String)
+        this
+    }
+
+    TestClassExecutionResult assertTestFailed(String name, Matcher<? super String> messageMatcher) {
+        def testMethodNode = findTestMethod(name)
+        assertEquals('FAIL', testMethodNode. at status as String)
+        assertThat(testMethodNode.exception[0].message[0].text().trim(), messageMatcher)
+        this
+    }
+
+    TestClassExecutionResult assertStdout(Matcher<? super String> matcher) {
+        throw new UnsupportedOperationException();
+    }
+
+    TestClassExecutionResult assertStderr(Matcher<? super String> matcher) {
+        throw new UnsupportedOperationException();
+    }
+
+    TestClassExecutionResult assertConfigMethodPassed(String name) {
+        def testMethodNode = findConfigMethod(name)
+        assertEquals('PASS', testMethodNode. at status as String)
+        this
+    }
+
+    TestClassExecutionResult assertConfigMethodFailed(String name) {
+        def testMethodNode = findConfigMethod(name)
+        assertEquals('FAIL', testMethodNode. at status as String)
+        this
+    }
+
+    private def findConfigMethod(String testName) {
+        def testMethods = findConfigMethods()
+        if (!testMethods.containsKey(testName)) {
+            fail("Could not find configuration method ${testClass}.${testName}. Found ${testMethods.keySet()}")
+        }
+        testMethods[testName]
+    }
+
+    private def findConfigMethods() {
+        Map testMethods = [:]
+        testClassNode.'test-method'.findAll { it.'@is-config' == 'true' }.each {
+            testMethods.put(it. at name as String, it)
+        }
+        testMethods
+    }
+
+    private def findTestMethod(String testName) {
+        def testMethods = findTestMethods()
+        if (!testMethods.containsKey(testName)) {
+            fail("Could not find test ${testClass}.${testName}. Found ${testMethods.keySet()}")
+        }
+        testMethods[testName]
+    }
+
+    private def findTestMethods() {
+        Map testMethods = [:]
+        testClassNode.'test-method'.findAll { it.'@is-config' != 'true' }.each {
+            testMethods.put(it. at name as String, it)
+        }
+        testMethods
+    }
+
+}
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy
index dbdf762..e853130 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationProject.groovy
@@ -59,9 +59,9 @@ public class TestNGIntegrationProject {
 
     void doAssert(projectDir, result) {
         if (assertClosure.maximumNumberOfParameters == 3) {
-            assertClosure(name, projectDir, new TestNgExecutionResult(projectDir))
+            assertClosure(name, projectDir, new TestNGExecutionResult(projectDir))
         } else {
-            assertClosure(name, projectDir, new TestNgExecutionResult(projectDir), result)
+            assertClosure(name, projectDir, new TestNGExecutionResult(projectDir), result)
         }
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
index bf32f54..4fcd9a6 100644
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
+++ b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
@@ -17,165 +17,42 @@
 
 package org.gradle.integtests.testng
 
-import org.gradle.api.Project
 import org.gradle.integtests.DistributionIntegrationTestRunner
 import org.gradle.integtests.fixtures.ExecutionResult
 import org.gradle.integtests.fixtures.GradleDistribution
 import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.TestExecutionResult
-import org.gradle.util.TestFile
-import org.junit.Ignore
+import org.gradle.integtests.fixtures.TestResources
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import static org.gradle.integtests.testng.TestNGIntegrationProject.*
-import static org.gradle.util.Matchers.*
+import static org.gradle.util.Matchers.containsLine
 import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
+import static org.junit.Assert.assertThat
 
 /**
  * @author Tom Eyckmans
  */
 @RunWith(DistributionIntegrationTestRunner.class)
 public class TestNGIntegrationTest {
-    static final String GROOVY = "groovy"
-    static final String JAVA = "java"
-    static final String JDK14 = "jdk14"
-    static final String JDK15 = "jdk15"
-
-    static final GROOVY_JDK15_FAILING = failingIntegrationProject(GROOVY, JDK15, { name, projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.BadTest')
-        result.testClass('org.gradle.BadTest').assertTestFailed('failingTest', equalTo('broken'))
-    })
-    static final GROOVY_JDK15_PASSING = passingIntegrationProject(GROOVY, JDK15, { name, TestFile projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.OkTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
-    })
-    static final JAVA_JDK14_FAILING = failingIntegrationProject(JAVA, JDK14, { name, projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.BadTest')
-        result.testClass('org.gradle.BadTest').assertTestFailed('failingTest', equalTo('broken'))
-    })
-    static final JAVA_JDK14_PASSING = passingIntegrationProject(JAVA, JDK14, { name, projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.OkTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
-    })
-    static final JAVA_JDK15_FAILING = failingIntegrationProject(JAVA, JDK15, { name, projectDir, TestExecutionResult result, ExecutionResult execution ->
-        result.assertTestClassesExecuted('org.gradle.BadTest', 'org.gradle.TestWithBrokenSetup', 'org.gradle.BrokenAfterSuite')
-        result.testClass('org.gradle.BadTest').assertTestFailed('failingTest', equalTo('broken'))
-        result.testClass('org.gradle.TestWithBrokenSetup').assertConfigMethodFailed('setup')
-        result.testClass('org.gradle.BrokenAfterSuite').assertConfigMethodFailed('cleanup')
-        assertThat(execution.error, containsString('Test org.gradle.BadTest FAILED'))
-        assertThat(execution.error, containsString('Test org.gradle.TestWithBrokenSetup FAILED'))
-        assertThat(execution.error, containsString('Test org.gradle.BrokenAfterSuite FAILED'))
-    })
-    static final JAVA_JDK15_PASSING = passingIntegrationProject(JAVA, JDK15, { name, projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.ConcreteTest', 'org.gradle.SuiteSetup', 'org.gradle.SuiteCleanup', 'org.gradle.TestSetup', 'org.gradle.TestCleanup')
-        result.testClass('org.gradle.OkTest').assertTestsExecuted('passingTest', 'expectedFailTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('expectedFailTest')
-        result.testClass('org.gradle.ConcreteTest').assertTestsExecuted('ok', 'alsoOk')
-        result.testClass('org.gradle.ConcreteTest').assertTestPassed('ok')
-        result.testClass('org.gradle.ConcreteTest').assertTestPassed('alsoOk')
-        result.testClass('org.gradle.SuiteSetup').assertConfigMethodPassed('setupSuite')
-        result.testClass('org.gradle.SuiteCleanup').assertConfigMethodPassed('cleanupSuite')
-        result.testClass('org.gradle.TestSetup').assertConfigMethodPassed('setupTest')
-        result.testClass('org.gradle.TestCleanup').assertConfigMethodPassed('cleanupTest')
-    })
-    static final JAVA_JDK15_PASSING_NO_REPORT = passingIntegrationProject(JAVA, JDK15, "-no-report", { name, TestFile projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.OkTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
-        projectDir.file('build/reports/tests/index.html').assertDoesNotExist()
-    })
-    static final SUITE_XML_BUILDER = new TestNGIntegrationProject("suitexmlbuilder", false, null, { name, projectDir, TestExecutionResult result ->
-        result.assertTestClassesExecuted('org.gradle.testng.UserImplTest')
-        result.testClass('org.gradle.testng.UserImplTest').assertTestsExecuted('testOkFirstName')
-        result.testClass('org.gradle.testng.UserImplTest').assertTestPassed('testOkFirstName')
-    })
-
     @Rule public final GradleDistribution dist = new GradleDistribution()
     @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources resources = new TestResources()
 
     @Test
     public void executesTestsInCorrectEnvironment() {
-        TestFile testDir = dist.testDir;
-        TestFile buildFile = testDir.file('build.gradle');
-        buildFile << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { testCompile 'org.testng:testng:5.8:jdk15' }
-            test {
-                useTestNG()
-                systemProperties.testSysProperty = 'value'
-                environment.TEST_ENV_VAR = 'value'
-            }
-        '''
-        testDir.file("src/test/java/org/gradle/OkTest.java") << """
-            package org.gradle;
-            import java.util.logging.Logger;
-            import static org.testng.Assert.*;
-            public class OkTest {
-                @org.testng.annotations.Test public void ok() throws Exception {
-                    // check working dir
-                    assertEquals("${testDir.absolutePath.replaceAll('\\\\', '\\\\\\\\')}", System.getProperty("user.dir"));
-                    // check Gradle classes not visible
-                    try { getClass().getClassLoader().loadClass("${Project.class.getName()}"); fail(); } catch(ClassNotFoundException e) { }
-                    // check context classloader
-                    assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader());
-                    // check sys properties
-                    assertEquals("value", System.getProperty("testSysProperty"));
-                    // check env vars
-                    assertEquals("value", System.getenv("TEST_ENV_VAR"));
-                    // check logging
-                    System.out.println("stdout");
-                    System.err.println("stderr");
-                    Logger.getLogger("test").warning("a warning");
-                }
-            }
-        """
-
-        ExecutionResult result = executer.withTasks('build').withArguments('-s').run();
+        ExecutionResult result = executer.withTasks('test').run();
+
         assertThat(result.output, not(containsString('stdout')))
         assertThat(result.error, not(containsString('stderr')))
         assertThat(result.error, not(containsString('a warning')))
 
-        new TestNgExecutionResult(testDir).testClass('org.gradle.OkTest').assertTestPassed('ok')
+        new TestNGExecutionResult(dist.testDir).testClass('org.gradle.OkTest').assertTestPassed('ok')
     }
 
     @Test
-    public void canListenerForTestResults() {
-        TestFile testDir = dist.getTestDir();
-        testDir.file('src/main/java/AppException.java').writelns(
-                "public class AppException extends Exception { }"
-        );
-
-        testDir.file('src/test/java/SomeTest.java').writelns(
-                "public class SomeTest {",
-                "@org.testng.annotations.Test public void pass() { }",
-                "@org.testng.annotations.Test public void fail() { assert false; }",
-                "@org.testng.annotations.Test public void knownError() { throw new RuntimeException(\"message\"); }",
-                "@org.testng.annotations.Test public void unknownError() throws AppException { throw new AppException(); }",
-                "}"
-        );
-
-        testDir.file('build.gradle') << '''
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies { testCompile 'org.testng:testng:5.8:jdk15' }
-            def listener = new TestListenerImpl()
-            test {
-                useTestNG()
-                addTestListener(listener)
-                ignoreFailures = true
-            }
-            class TestListenerImpl implements TestListener {
-                void beforeSuite(TestDescriptor suite) { println "START [$suite] [$suite.name]" }
-                void afterSuite(TestDescriptor suite, TestResult result) { println "FINISH [$suite] [$suite.name]" }
-                void beforeTest(TestDescriptor test) { println "START [$test] [$test.name]" }
-                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [$test] [$test.name] [$result.error]" }
-            }
-        '''
-
+    public void canListenForTestResults() {
         ExecutionResult result = executer.withTasks("test").run();
+
         assertThat(result.getOutput(), containsLine("START [tests] []"));
         assertThat(result.getOutput(), containsLine("FINISH [tests] []"));
         assertThat(result.getOutput(), containsLine("START [test process 'Gradle Worker 1'] [Gradle Worker 1]"));
@@ -193,47 +70,46 @@ public class TestNGIntegrationTest {
     }
 
     @Test
-    public void suiteXmlBuilder() {
-        checkProject(SUITE_XML_BUILDER)
-    }
+    public void groovyJdk15Failing() {
+        executer.withTasks("test").runWithFailure().assertThatCause(startsWith('There were failing tests'))
 
-    @Test
-    public void groovyJdk15() {
-        checkProject(GROOVY_JDK15_FAILING)
-        checkProject(GROOVY_JDK15_PASSING)
+        def result = new TestNGExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.BadTest')
+        result.testClass('org.gradle.BadTest').assertTestFailed('failingTest', equalTo('broken'))
     }
 
     @Test
-    public void javaJdk14() {
-        checkProject(JAVA_JDK14_PASSING)
-        checkProject(JAVA_JDK14_FAILING)
+    public void groovyJdk15Passing() {
+        executer.withTasks("test").run()
+
+        def result = new TestNGExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.OkTest')
+        result.testClass('org.gradle.OkTest').assertTestPassed('passingTest')
     }
 
     @Test
-    public void javaJdk15() {
-        checkProject(JAVA_JDK15_PASSING)
-        checkProject(JAVA_JDK15_FAILING)
-    }
+    public void javaJdk14Failing() {
+        executer.withTasks("test").runWithFailure().assertThatCause(startsWith('There were failing tests'))
 
-    @Ignore
-    public void javaJdk15WithNoReports() {
-        // TODO currently reports are always generated because the antTestNGExecute task uses the
-        // default listeners and these generate reports by default. Enable the test when this has changed.
-        checkProject(JAVA_JDK15_PASSING_NO_REPORT)
+        def result = new TestNGExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.BadTest')
+        result.testClass('org.gradle.BadTest').assertTestFailed('failingTest', equalTo('broken'))
     }
 
-    private def checkProject(TestNGIntegrationProject project) {
-        final File projectDir = dist.samplesDir.file("testng", project.name)
-
-        def result
-        executer.inDirectory(projectDir).withTasks('clean', 'test')
-        if (project.expectFailure) {
-            result = executer.runWithFailure()
-        } else {
-            result = executer.run()
-        }
+    @Test
+    public void javaJdk15Failing() {
+        def execution = executer.withTasks("test").runWithFailure().assertThatCause(startsWith('There were failing tests'))
 
-        // output: output, error: error, command: actualCommand, unixCommand: unixCommand, windowsCommand: windowsCommand
-        project.doAssert(projectDir, result)
+        def result = new TestNGExecutionResult(dist.testDir)
+        result.assertTestClassesExecuted('org.gradle.BadTest', 'org.gradle.TestWithBrokenSetup', 'org.gradle.BrokenAfterSuite', 'org.gradle.TestWithBrokenMethodDependency')
+        result.testClass('org.gradle.BadTest').assertTestFailed('failingTest', equalTo('broken'))
+        result.testClass('org.gradle.TestWithBrokenSetup').assertConfigMethodFailed('setup')
+        result.testClass('org.gradle.BrokenAfterSuite').assertConfigMethodFailed('cleanup')
+        result.testClass('org.gradle.TestWithBrokenMethodDependency').assertTestFailed('broken', equalTo('broken'))
+        result.testClass('org.gradle.TestWithBrokenMethodDependency').assertTestSkipped('okTest')
+        assertThat(execution.error, containsString('Test org.gradle.BadTest FAILED'))
+        assertThat(execution.error, containsString('Test org.gradle.TestWithBrokenSetup FAILED'))
+        assertThat(execution.error, containsString('Test org.gradle.BrokenAfterSuite FAILED'))
+        assertThat(execution.error, containsString('Test org.gradle.TestWithBrokenMethodDependency FAILED'))
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNgExecutionResult.groovy b/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNgExecutionResult.groovy
deleted file mode 100644
index 734c8ec..0000000
--- a/subprojects/gradle-core/src/integTest/groovy/org/gradle/integtests/testng/TestNgExecutionResult.groovy
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.gradle.integtests.testng
-
-import groovy.util.slurpersupport.GPathResult
-import org.gradle.integtests.fixtures.TestClassExecutionResult
-import org.gradle.integtests.fixtures.TestExecutionResult
-import org.gradle.util.TestFile
-import org.hamcrest.Matcher
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-
-class TestNgExecutionResult implements TestExecutionResult {
-    private final TestFile projectDir
-    private final GPathResult resultsXml
-
-    def TestNgExecutionResult(projectDir) {
-        this.projectDir = projectDir;
-        resultsXml = new XmlSlurper().parse(projectDir.file('build/reports/tests/testng-results.xml').assertIsFile())
-    }
-
-    TestExecutionResult assertTestClassesExecuted(String... testClasses) {
-        projectDir.file('build/reports/tests/index.html').assertIsFile()
-        def actualTestClasses = findTestClasses().keySet()
-        assertThat(actualTestClasses, equalTo(testClasses as Set))
-        this
-    }
-
-    TestClassExecutionResult testClass(String testClass) {
-        return new TestNgTestClassExecutionResult(testClass, findTestClass(testClass))
-    }
-
-    private def findTestClass(String testClass) {
-        def testClasses = findTestClasses()
-        if (!testClasses.containsKey(testClass)) {
-            fail("Could not find test class ${testClass}. Found ${testClasses.keySet()}")
-        }
-        testClasses[testClass]
-    }
-
-    private def findTestClasses() {
-        Map testClasses = [:]
-        resultsXml.suite.test.'class'.each {
-            testClasses.put(it. at name as String, it)
-        }
-        testClasses
-    }
-}
-
-private class TestNgTestClassExecutionResult implements TestClassExecutionResult {
-    def String testClass
-    def GPathResult testClassNode
-
-    def TestNgTestClassExecutionResult(String testClass, GPathResult resultXml) {
-        this.testClass = testClass
-        this.testClassNode = resultXml
-    }
-
-    TestClassExecutionResult assertTestsExecuted(String... testNames) {
-        def actualTestMethods = findTestMethods().keySet()
-        assertThat(actualTestMethods, equalTo(testNames as Set))
-        this
-    }
-
-    TestClassExecutionResult assertTestPassed(String name) {
-        def testMethodNode = findTestMethod(name)
-        assertEquals('PASS', testMethodNode. at status as String)
-        this
-    }
-
-    TestClassExecutionResult assertTestFailed(String name, Matcher<? super String> messageMatcher) {
-        def testMethodNode = findTestMethod(name)
-        assertEquals('FAIL', testMethodNode. at status as String)
-        assertThat(testMethodNode.exception[0].message[0].text().trim(), messageMatcher)
-        this
-    }
-
-    TestClassExecutionResult assertStdout(Matcher<? super String> matcher) {
-        throw new UnsupportedOperationException();
-    }
-
-    TestClassExecutionResult assertStderr(Matcher<? super String> matcher) {
-        throw new UnsupportedOperationException();
-    }
-
-    TestClassExecutionResult assertConfigMethodPassed(String name) {
-        def testMethodNode = findConfigMethod(name)
-        assertEquals('PASS', testMethodNode. at status as String)
-        this
-    }
-
-    TestClassExecutionResult assertConfigMethodFailed(String name) {
-        def testMethodNode = findConfigMethod(name)
-        assertEquals('FAIL', testMethodNode. at status as String)
-        this
-    }
-
-    private def findConfigMethod(String testName) {
-        def testMethods = findConfigMethods()
-        if (!testMethods.containsKey(testName)) {
-            fail("Could not find configuration method ${testClass}.${testName}. Found ${testMethods.keySet()}")
-        }
-        testMethods[testName]
-    }
-
-    private def findConfigMethods() {
-        Map testMethods = [:]
-        testClassNode.'test-method'.findAll { it.'@is-config' == 'true' }.each {
-            testMethods.put(it. at name as String, it)
-        }
-        testMethods
-    }
-
-    private def findTestMethod(String testName) {
-        def testMethods = findTestMethods()
-        if (!testMethods.containsKey(testName)) {
-            fail("Could not find test ${testClass}.${testName}. Found ${testMethods.keySet()}")
-        }
-        testMethods[testName]
-    }
-
-    private def findTestMethods() {
-        Map testMethods = [:]
-        testClassNode.'test-method'.findAll { it.'@is-config' != 'true' }.each {
-            testMethods.put(it. at name as String, it)
-        }
-        testMethods
-    }
-
-}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle
index 7cd52a9..e4374f4 100644
--- a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/ArtifactDependenciesIntegrationTest/canHaveCycleInProjectDependencies/build.gradle
@@ -1,9 +1,9 @@
-import org.junit.Assert
 
 subprojects {
     apply plugin: 'base'
     configurations {
         'default'
+        other
     }
     task jar(type: Jar)
     artifacts {
@@ -14,12 +14,17 @@ subprojects {
 project('a') {
     dependencies {
         'default' project(':b')
+        other project(':b')
     }
     task listJars {
-        dependsOn configurations['default']
+        dependsOn configurations.default
+        dependsOn configurations.other
         doFirst {
-            def jars = configurations['default'].collect { it.name } as Set
-            Assert.assertEquals(['b.jar', 'c.jar'] as Set, jars)
+            def jars = configurations.default.collect { it.name } as Set
+            assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
+
+            jars = configurations.other.collect { it.name } as Set
+            assert jars == ['a.jar', 'b.jar', 'c.jar'] as Set
         }
     }
 }
@@ -32,6 +37,6 @@ project('b') {
 
 project('c') {
     dependencies {
-        'default' project(':b')
+        'default' project(':a')
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
index dd769ba..625b689 100644
--- a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/api/build.gradle
@@ -2,10 +2,6 @@ dependencies {
     runtime 'commons-collections:commons-collections:3.2 at jar'
 }
 
-ideaModule.doLast {
-    compareXmlWithIgnoringOrder(getExpectedXml(project, 'expectedApiModule.xml'), file("api.iml").text)
-}
-
 cleanIdea.doLast {
     assert !file("api/api.iml").isFile()
 }
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
index 42b0cee..74c082e 100644
--- a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
@@ -1,16 +1,3 @@
-import org.custommonkey.xmlunit.Diff
-import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier
-import org.custommonkey.xmlunit.XMLAssert
-
-buildscript {
-    repositories {
-        mavenCentral()
-    }
-    dependencies {
-        classpath 'xmlunit:xmlunit:1.3'
-    }
-}
-
 defaultTasks 'idea', 'cleanIdea'
 
 allprojects {
@@ -36,36 +23,8 @@ subprojects {
     version = '1.0'
 }
 
-ideaModule.doLast {
-    compareXmlWithIgnoringOrder(file('expectedFiles/expectedRootModule.xml').text,
-            file("${project.name}.iml").text)
-}
-
-ideaProject.doLast {
-    compareXmlWithIgnoringOrder(file('expectedFiles/expectedProjectFile.xml').text,
-            file("${project.name}.ipr").text)
-}
-
-ideaWorkspace.doLast {
-    compareXmlWithIgnoringOrder(file('expectedFiles/expectedIwsFile.xml').text,
-            file("${project.name}.iws").text )
-}
-
 cleanIdea.doLast {
     assert !file("${project.name}.iml").isFile()
     assert !file("${project.name}.ipr").isFile()
     assert !file("${project.name}.iws").isFile()
 }
-
-void compareXmlWithIgnoringOrder(String expectedXml, String actualXml) {
-    Diff diff = new Diff(expectedXml, actualXml)
-    diff.overrideElementQualifier(new ElementNameAndAttributeQualifier())
-    XMLAssert.assertXMLEqual(diff, true);
-}
-
-String getExpectedXml(Project subProject, String filename) {
-    def cache = new File(gradle.gradleUserHomeDir, "/cache")
-    def path = org.gradle.plugins.idea.model.Path.getRelativePath(subProject.projectDir, '$MODULE_DIR$', cache)
-    return rootProject.file("expectedFiles/$filename").text.replace('@CACHE_DIR@', path)
-}
-
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedApiModule.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml
similarity index 100%
rename from subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedApiModule.xml
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/api/api.iml.xml
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedRootModule.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml
similarity index 100%
copy from subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedRootModule.xml
copy to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iml.xml
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedProjectFile.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.ipr.xml
similarity index 100%
rename from subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedProjectFile.xml
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.ipr.xml
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedIwsFile.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iws.xml
similarity index 100%
rename from subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedIwsFile.xml
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/root.iws.xml
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedWebserviceModule.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml
similarity index 100%
rename from subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedWebserviceModule.xml
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/webservice/webservice.iml.xml
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
index 0477c28..c1c707b 100644
--- a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/webservice/build.gradle
@@ -10,10 +10,6 @@ dependencies {
     }
 }
 
-ideaModule.doLast {
-    compareXmlWithIgnoringOrder(getExpectedXml(project, 'expectedWebserviceModule.xml'), file("webservice.iml").text)
-}
-
 cleanIdea.doLast {
     assert !file("webservice/webservice.iml").isFile()
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle
new file mode 100644
index 0000000..f8e3ff8
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/build.gradle
@@ -0,0 +1,4 @@
+apply plugin: 'idea'
+project('a') {
+    apply plugin: 'idea'
+}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml
new file mode 100644
index 0000000..e62560c
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/expectedFiles/root.ipr.xml
@@ -0,0 +1,103 @@
+<project version="4">
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac"/>
+    <resourceExtensions>
+      <entry name=".+\.(properties|xml|html|dtd|tld)"/>
+      <entry name=".+\.(gif|png|jpeg|jpg)"/>
+    </resourceExtensions>
+    <annotationProcessing enabled="false" useClasspath="true"/>
+    <wildcardResourcePatterns>
+      <entry name="!?*.groovy"/>
+      <entry name="!?*.java"/>
+    </wildcardResourcePatterns>
+  </component>
+  <component name="CopyrightManager" default="">
+    <module2copyright/>
+  </component>
+  <component name="DependencyValidationManager">
+    <option name="SKIP_IMPORT_STATEMENTS" value="false"/>
+  </component>
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"/>
+  <component name="GradleUISettings">
+    <setting name="root"/>
+  </component>
+  <component name="GradleUISettings2">
+    <setting name="root"/>
+  </component>
+  <component name="IdProvider" IDEtalkID="11DA1DB66DD62DDA1ED602B7079FE97C"/>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY"/>
+    <option name="OPTION_SCOPE" value="protected"/>
+    <option name="OPTION_HIERARCHY" value="true"/>
+    <option name="OPTION_NAVIGATOR" value="true"/>
+    <option name="OPTION_INDEX" value="true"/>
+    <option name="OPTION_SEPARATE_INDEX" value="true"/>
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true"/>
+    <option name="OPTION_DEPRECATED_LIST" value="true"/>
+    <option name="OTHER_OPTIONS" value=""/>
+    <option name="HEAP_SIZE"/>
+    <option name="LOCALE"/>
+    <option name="OPEN_IN_BROWSER" value="true"/>
+  </component>
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/root.iml" filepath="$PROJECT_DIR$/root.iml"/>
+      <module fileurl="file://$PROJECT_DIR$/a/a.iml" filepath="$PROJECT_DIR$/a/a.iml"/>
+    </modules>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-type="JavaSDK" assert-jdk-15="true" project-jdk-name="1.6">
+    <output url="file://$PROJECT_DIR$/out"/>
+  </component>
+  <component name="SvnBranchConfigurationManager">
+    <option name="mySupportsUserInfoFilter" value="true"/>
+  </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs=""/>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ArtifactsStructureConfigurable.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+          <settings/>
+        </UIState>
+      </state>
+      <state key="Copyright.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+        </UIState>
+      </state>
+      <state key="ProjectJDKs.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl>
+              <option name="proportions">
+                <list>
+                  <option value="0.2"/>
+                </list>
+              </option>
+            </SplitterProportionsDataImpl>
+          </splitter-proportions>
+          <last-edited>
+            1.6
+          </last-edited>
+        </UIState>
+      </state>
+      <state key="ScopeChooserConfigurable.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+          <settings/>
+        </UIState>
+      </state>
+    </states>
+  </component>
+</project>
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle
new file mode 100644
index 0000000..eb9dd6f
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithASubProjectThatDoesNotHaveTheIdeaPluginApplied/settings.gradle
@@ -0,0 +1,3 @@
+include 'a', 'b'
+
+rootProject.name = 'root'
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle
new file mode 100644
index 0000000..4475bd5
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/build.gradle
@@ -0,0 +1 @@
+apply plugin: 'idea'
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedRootModule.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml
similarity index 100%
rename from subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/canCreateAndDeleteMetaData/expectedFiles/expectedRootModule.xml
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.iml.xml
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml
new file mode 100644
index 0000000..aa1d7b1
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/expectedFiles/root.ipr.xml
@@ -0,0 +1,102 @@
+<project version="4">
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac"/>
+    <resourceExtensions>
+      <entry name=".+\.(properties|xml|html|dtd|tld)"/>
+      <entry name=".+\.(gif|png|jpeg|jpg)"/>
+    </resourceExtensions>
+    <annotationProcessing enabled="false" useClasspath="true"/>
+    <wildcardResourcePatterns>
+      <entry name="!?*.groovy"/>
+      <entry name="!?*.java"/>
+    </wildcardResourcePatterns>
+  </component>
+  <component name="CopyrightManager" default="">
+    <module2copyright/>
+  </component>
+  <component name="DependencyValidationManager">
+    <option name="SKIP_IMPORT_STATEMENTS" value="false"/>
+  </component>
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"/>
+  <component name="GradleUISettings">
+    <setting name="root"/>
+  </component>
+  <component name="GradleUISettings2">
+    <setting name="root"/>
+  </component>
+  <component name="IdProvider" IDEtalkID="11DA1DB66DD62DDA1ED602B7079FE97C"/>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY"/>
+    <option name="OPTION_SCOPE" value="protected"/>
+    <option name="OPTION_HIERARCHY" value="true"/>
+    <option name="OPTION_NAVIGATOR" value="true"/>
+    <option name="OPTION_INDEX" value="true"/>
+    <option name="OPTION_SEPARATE_INDEX" value="true"/>
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true"/>
+    <option name="OPTION_DEPRECATED_LIST" value="true"/>
+    <option name="OTHER_OPTIONS" value=""/>
+    <option name="HEAP_SIZE"/>
+    <option name="LOCALE"/>
+    <option name="OPEN_IN_BROWSER" value="true"/>
+  </component>
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/root.iml" filepath="$PROJECT_DIR$/root.iml"/>
+    </modules>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-type="JavaSDK" assert-jdk-15="true" project-jdk-name="1.6">
+    <output url="file://$PROJECT_DIR$/out"/>
+  </component>
+  <component name="SvnBranchConfigurationManager">
+    <option name="mySupportsUserInfoFilter" value="true"/>
+  </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs=""/>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ArtifactsStructureConfigurable.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+          <settings/>
+        </UIState>
+      </state>
+      <state key="Copyright.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+        </UIState>
+      </state>
+      <state key="ProjectJDKs.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl>
+              <option name="proportions">
+                <list>
+                  <option value="0.2"/>
+                </list>
+              </option>
+            </SplitterProportionsDataImpl>
+          </splitter-proportions>
+          <last-edited>
+            1.6
+          </last-edited>
+        </UIState>
+      </state>
+      <state key="ScopeChooserConfigurable.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+          <settings/>
+        </UIState>
+      </state>
+    </states>
+  </component>
+</project>
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle
new file mode 100644
index 0000000..b054ea8
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithAnEmptyProject/settings.gradle
@@ -0,0 +1,2 @@
+
+rootProject.name = 'root'
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml
new file mode 100644
index 0000000..3576ff1
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.iml.xml
@@ -0,0 +1,18 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false"/>
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false"/>
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true"/>
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true"/>
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+    <output url="file://$MODULE_DIR$/build/classes/main"/>
+    <output-test url="file://$MODULE_DIR$/build/classes/test"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml
new file mode 100644
index 0000000..dadf9fb
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/root/root.ipr.xml
@@ -0,0 +1,104 @@
+<project version="4">
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac"/>
+    <resourceExtensions>
+      <entry name=".+\.(properties|xml|html|dtd|tld)"/>
+      <entry name=".+\.(gif|png|jpeg|jpg)"/>
+    </resourceExtensions>
+    <annotationProcessing enabled="false" useClasspath="true"/>
+    <wildcardResourcePatterns>
+      <entry name="!?*.groovy"/>
+      <entry name="!?*.java"/>
+    </wildcardResourcePatterns>
+  </component>
+  <component name="CopyrightManager" default="">
+    <module2copyright/>
+  </component>
+  <component name="DependencyValidationManager">
+    <option name="SKIP_IMPORT_STATEMENTS" value="false"/>
+  </component>
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"/>
+  <component name="GradleUISettings">
+    <setting name="root"/>
+  </component>
+  <component name="GradleUISettings2">
+    <setting name="root"/>
+  </component>
+  <component name="IdProvider" IDEtalkID="11DA1DB66DD62DDA1ED602B7079FE97C"/>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY"/>
+    <option name="OPTION_SCOPE" value="protected"/>
+    <option name="OPTION_HIERARCHY" value="true"/>
+    <option name="OPTION_NAVIGATOR" value="true"/>
+    <option name="OPTION_INDEX" value="true"/>
+    <option name="OPTION_SEPARATE_INDEX" value="true"/>
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false"/>
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true"/>
+    <option name="OPTION_DEPRECATED_LIST" value="true"/>
+    <option name="OTHER_OPTIONS" value=""/>
+    <option name="HEAP_SIZE"/>
+    <option name="LOCALE"/>
+    <option name="OPEN_IN_BROWSER" value="true"/>
+  </component>
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/../top-level.iml" filepath="$PROJECT_DIR$/../top-level.iml"/>
+      <module fileurl="file://$PROJECT_DIR$/root.iml" filepath="$PROJECT_DIR$/root.iml"/>
+      <module fileurl="file://$PROJECT_DIR$/../a child project/a child.iml" filepath="$PROJECT_DIR$/../a child project/a child.iml"/>
+    </modules>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-type="JavaSDK" assert-jdk-15="true" project-jdk-name="1.5">
+    <output url="file://$PROJECT_DIR$/out"/>
+  </component>
+  <component name="SvnBranchConfigurationManager">
+    <option name="mySupportsUserInfoFilter" value="true"/>
+  </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs=""/>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ArtifactsStructureConfigurable.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+          <settings/>
+        </UIState>
+      </state>
+      <state key="Copyright.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+        </UIState>
+      </state>
+      <state key="ProjectJDKs.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl>
+              <option name="proportions">
+                <list>
+                  <option value="0.2"/>
+                </list>
+              </option>
+            </SplitterProportionsDataImpl>
+          </splitter-proportions>
+          <last-edited>
+            1.6
+          </last-edited>
+        </UIState>
+      </state>
+      <state key="ScopeChooserConfigurable.UI">
+        <UIState>
+          <splitter-proportions>
+            <SplitterProportionsDataImpl/>
+          </splitter-proportions>
+          <settings/>
+        </UIState>
+      </state>
+    </states>
+  </component>
+</project>
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml
new file mode 100644
index 0000000..3576ff1
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/expectedFiles/top-level.iml.xml
@@ -0,0 +1,18 @@
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <exclude-output/>
+    <orderEntry type="inheritedJdk"/>
+    <content url="file://$MODULE_DIR$/">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false"/>
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false"/>
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true"/>
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true"/>
+      <excludeFolder url="file://$MODULE_DIR$/.gradle"/>
+      <excludeFolder url="file://$MODULE_DIR$/build"/>
+    </content>
+    <orderEntry type="sourceFolder" forTests="false"/>
+    <output url="file://$MODULE_DIR$/build/classes/main"/>
+    <output-test url="file://$MODULE_DIR$/build/classes/test"/>
+  </component>
+  <component name="ModuleRootManager"/>
+</module>
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
new file mode 100644
index 0000000..8b39aa1
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
@@ -0,0 +1,4 @@
+allprojects {
+    apply plugin: 'java'
+    apply plugin: 'idea'
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle
new file mode 100644
index 0000000..9d62d9a
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IdeaIntegrationTest/worksWithNonStandardLayout/settings.gradle
@@ -0,0 +1,6 @@
+include 'a child', 'top-level'
+
+rootProject.name = 'root'
+rootProject.projectDir = file('root')
+project(':top-level').projectDir = settingsDir
+project(':a child').projectDir = file('a child project')
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
index 31b3cc5..f364acc 100644
--- a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/IncrementalTestIntegrationTest/executesTestsWhenSelectedTestsChange/build.gradle
@@ -6,7 +6,7 @@ repositories {
 
 dependencies {
     testCompile 'junit:junit:4.7'
-    testCompile 'org.testng:testng:5.12.1'
+    testCompile 'org.testng:testng:5.13.1'
 }
 
 test {
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
new file mode 100644
index 0000000..1efaa24
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
@@ -0,0 +1,21 @@
+apply plugin: 'java'
+
+test {
+    include '**/*Test1.*'
+}
+
+task test2(type: Test) {
+    include '**/*Test2.*'
+}
+
+check {
+    dependsOn test2
+}
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'junit:junit:4.8.1'
+}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
new file mode 100644
index 0000000..e0deefb
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
@@ -0,0 +1,9 @@
+package org.gradle;
+
+import org.junit.Test;
+
+public class Test1 {
+    @Test
+    public void ok() {
+    }
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java
new file mode 100644
index 0000000..32b1274
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test2.java
@@ -0,0 +1,9 @@
+package org.gradle;
+
+import org.junit.Test;
+
+public class Test2 {
+    @Test
+    public void ok() {
+    }
+}
diff --git a/subprojects/gradle-docs/src/samples/logging/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/build.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/build.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/build.gradle
diff --git a/subprojects/gradle-docs/src/samples/logging/buildSrc/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/buildSrc/build.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/buildSrc/build.gradle
diff --git a/subprojects/gradle-docs/src/samples/logging/external.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/external.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/external.gradle
diff --git a/subprojects/gradle-docs/src/samples/logging/init.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/init.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/init.gradle
diff --git a/subprojects/gradle-docs/src/samples/logging/nestedBuild/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/build.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/nestedBuild/build.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/build.gradle
diff --git a/subprojects/gradle-docs/src/samples/logging/nestedBuild/buildSrc/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/nestedBuild/buildSrc/build.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/nestedBuild/buildSrc/build.gradle
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle
new file mode 100644
index 0000000..ca46ce9
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project1/build.gradle
@@ -0,0 +1,77 @@
+import org.gradle.logging.StyledTextOutputFactory
+
+logger.quiet('An info log message which is always logged.')
+logger.error('An error log message.')
+logger.warn('A warning log message.')
+logger.lifecycle('A lifecycle info log message.')
+logger.info('An info log message.')
+logger.debug('A debug log message.')
+logger.trace('A trace log message.')
+
+println 'A message which is logged at QUIET level'
+
+// Should use stdout capture level
+def textOutput = services.get(StyledTextOutputFactory.class).create('build')
+textOutput.text('Text which is logged at QUIET level').println()
+
+logging.captureStandardOutput LogLevel.INFO
+println 'A message which is logged at INFO level'
+
+textOutput.text('Text which is logged at INFO level').println()
+
+System.err.println 'An error message which is logged at ERROR level'
+logging.captureStandardError LogLevel.LIFECYCLE
+System.err.println 'An error message which is logged at LIFECYCLE level'
+
+task logLifecycle {
+    logging.captureStandardOutput LogLevel.LIFECYCLE
+    logging.captureStandardError LogLevel.WARN
+    doFirst {
+        println('A task message which is logged at LIFECYCLE level')
+        System.err.println('A task error message which is logged at WARN level')
+    }
+}
+
+// START SNIPPET task-capture-stdout
+task logInfo {
+    logging.captureStandardOutput LogLevel.INFO
+    doFirst {
+        println 'A task message which is logged at INFO level'
+    }
+}
+// END SNIPPET task-capture-stdout
+
+task nestedBuildLog << {
+    def startParam = project.gradle.startParameter.newBuild()
+    startParam.currentDir = rootProject.file('nestedBuild')
+    startParam.taskNames = ['log']
+    GradleLauncher.newInstance(startParam).run().rethrowFailure()
+}
+
+task log(dependsOn: [logInfo, logLifecycle, nestedBuildLog]) << {
+    println('A task message which is logged at QUIET level')
+}
+
+// warn is the default log level for echo
+ant.echo('A warn message logged from Ant')
+ant.echo('An error message logged from Ant', level: org.apache.tools.ant.types.LogLevel.ERR)
+ant.echo('An info message logged from Ant', level: org.apache.tools.ant.types.LogLevel.INFO)
+ant.echo('A debug message logged from Ant', level: org.apache.tools.ant.types.LogLevel.DEBUG)
+
+// START SNIPPET use-slf4j
+org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger('some-logger')
+slf4jLogger.info('An info log message logged using SLF4j')
+// END SNIPPET use-slf4j
+
+org.apache.commons.logging.Log jclLogger = org.apache.commons.logging.LogFactory.getLog('some-logger')
+jclLogger.info('An info log message logged using JCL')
+
+org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger('some-logger')
+log4jLogger.info('An info log message logged using Log4j')
+
+java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger('some-logger')
+julLogger.severe('A severe log message logged using JUL')
+julLogger.warning('A warning log message logged using JUL')
+julLogger.info('An info log message logged using JUL')
+julLogger.config('A config log message logged using JUL')
+julLogger.fine('A fine log message logged using JUL')
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/logging/project2/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project2/build.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/project2/build.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/project2/build.gradle
diff --git a/subprojects/gradle-docs/src/samples/logging/settings.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/settings.gradle
similarity index 100%
rename from subprojects/gradle-docs/src/samples/logging/settings.gradle
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/logging/settings.gradle
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle
new file mode 100644
index 0000000..e3cc1be
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/LoggingIntegrationTest/multiThreaded/build.gradle
@@ -0,0 +1,26 @@
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import org.gradle.logging.StyledTextOutputFactory
+import static org.gradle.logging.StyledTextOutput.Style.*
+
+task log << {
+    def executor = Executors.newCachedThreadPool()
+    (1..10).each { thread ->
+        executor.execute {
+            def textOut = services.get(StyledTextOutputFactory.class).create('build')
+            (1..100).each { iteration ->
+                logger.lifecycle("log message from thread $thread iteration $iteration")
+                print "stdout message "
+                println "from thread $thread iteration $iteration"
+                textOut.text("styled text message from thread ")
+                textOut.style(UserInput).text(thread).style(Normal)
+                textOut.text(' iteration ')
+                textOut.style(UserInput).text(iteration).style(Normal)
+                textOut.println()
+            }
+        }
+    }
+
+    executor.shutdown()
+    executor.awaitTermination(30, TimeUnit.SECONDS)
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle
new file mode 100644
index 0000000..0a58919
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/build.gradle
@@ -0,0 +1,20 @@
+apply plugin: 'java'
+repositories { mavenCentral() }
+dependencies { testCompile 'org.testng:testng:5.13.1' }
+def listener = new TestListenerImpl()
+
+test {
+    useTestNG()
+    addTestListener(listener)
+    ignoreFailures = true
+}
+
+class TestListenerImpl implements TestListener {
+    void beforeSuite(TestDescriptor suite) { println "START [$suite] [$suite.name]" }
+
+    void afterSuite(TestDescriptor suite, TestResult result) { println "FINISH [$suite] [$suite.name]" }
+
+    void beforeTest(TestDescriptor test) { println "START [$test] [$test.name]" }
+
+    void afterTest(TestDescriptor test, TestResult result) { println "FINISH [$test] [$test.name] [$result.error]" }
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java
new file mode 100644
index 0000000..c79aabd
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/AppException.java
@@ -0,0 +1,2 @@
+public class AppException extends Exception {
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java
new file mode 100644
index 0000000..83d3e51
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/canListenForTestResults/src/test/java/SomeTest.java
@@ -0,0 +1,20 @@
+public class SomeTest {
+    @org.testng.annotations.Test
+    public void pass() {
+    }
+
+    @org.testng.annotations.Test
+    public void fail() {
+        assert false;
+    }
+
+    @org.testng.annotations.Test
+    public void knownError() {
+        throw new RuntimeException("message");
+    }
+
+    @org.testng.annotations.Test
+    public void unknownError() throws AppException {
+        throw new AppException();
+    }
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
new file mode 100644
index 0000000..897341d
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'java'
+repositories { mavenCentral() }
+dependencies { testCompile 'org.testng:testng:5.13.1' }
+test {
+    useTestNG()
+    systemProperties.testSysProperty = 'value'
+    systemProperties.testDir = projectDir
+    environment.TEST_ENV_VAR = 'value'
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
new file mode 100644
index 0000000..87566c9
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
@@ -0,0 +1,35 @@
+package org.gradle;
+
+import java.util.logging.Logger;
+
+import static org.testng.Assert.*;
+
+public class OkTest {
+    @org.testng.annotations.Test
+    public void ok() throws Exception {
+        // check working dir
+        assertEquals(System.getProperty("testDir"), System.getProperty("user.dir"));
+
+        // check Gradle classes not visible
+        try {
+            getClass().getClassLoader().loadClass("org.gradle.api.Project");
+            fail();
+        } catch (ClassNotFoundException e) {
+            // Expected
+        }
+
+        // check context classloader
+        assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader());
+
+        // check sys properties
+        assertEquals("value", System.getProperty("testSysProperty"));
+
+        // check env vars
+        assertEquals("value", System.getenv("TEST_ENV_VAR"));
+
+        // check logging
+        System.out.println("stdout");
+        System.err.println("stderr");
+        Logger.getLogger("test").warning("a warning");
+    }
+}
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
new file mode 100644
index 0000000..d72a901
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'groovy'
+
+sourceCompatibility=1.5
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+	groovy "org.codehaus.groovy:groovy-all:1.7.5"
+
+    testCompile 'org.testng:testng:5.13.1'
+}
+
+test {
+   useTestNG() 
+}
diff --git a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/src/main/groovy/org/gradle/Ok.groovy b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/main/groovy/org/gradle/Ok.groovy
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/src/main/groovy/org/gradle/Ok.groovy
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/main/groovy/org/gradle/Ok.groovy
diff --git a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/src/test/groovy/org/gradle/BadTest.groovy b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/test/groovy/org/gradle/BadTest.groovy
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/src/test/groovy/org/gradle/BadTest.groovy
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Failing/src/test/groovy/org/gradle/BadTest.groovy
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
new file mode 100644
index 0000000..d72a901
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'groovy'
+
+sourceCompatibility=1.5
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+	groovy "org.codehaus.groovy:groovy-all:1.7.5"
+
+    testCompile 'org.testng:testng:5.13.1'
+}
+
+test {
+   useTestNG() 
+}
diff --git a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/src/main/groovy/org/gradle/Ok.groovy b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/main/groovy/org/gradle/Ok.groovy
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/src/main/groovy/org/gradle/Ok.groovy
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/main/groovy/org/gradle/Ok.groovy
diff --git a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/src/test/groovy/org/gradle/OkTest.groovy b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/test/groovy/org/gradle/OkTest.groovy
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/src/test/groovy/org/gradle/OkTest.groovy
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/groovyJdk15Passing/src/test/groovy/org/gradle/OkTest.groovy
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle
new file mode 100644
index 0000000..0bc9c4e
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'java'
+
+sourceCompatibility=1.4
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'org.testng:testng:5.11:jdk14'
+}
+
+test {
+    useTestNG()
+    scanForTestClasses = false
+}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/src/main/java/org/gradle/Ok.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/main/java/org/gradle/Ok.java
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/src/main/java/org/gradle/Ok.java
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/main/java/org/gradle/Ok.java
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/src/test/java/org/gradle/BadTest.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/test/java/org/gradle/BadTest.java
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/src/test/java/org/gradle/BadTest.java
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk14Failing/src/test/java/org/gradle/BadTest.java
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle
new file mode 100644
index 0000000..d5769dc
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/build.gradle
@@ -0,0 +1,15 @@
+apply plugin: 'java'
+
+sourceCompatibility=1.5
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testCompile 'org.testng:testng:5.13.1'
+}
+
+test {
+   useTestNG()
+}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/main/java/org/gradle/Ok.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/main/java/org/gradle/Ok.java
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/main/java/org/gradle/Ok.java
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/main/java/org/gradle/Ok.java
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/test/java/org/gradle/BadTest.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BadTest.java
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/test/java/org/gradle/BadTest.java
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BadTest.java
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/test/java/org/gradle/BrokenAfterSuite.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BrokenAfterSuite.java
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/test/java/org/gradle/BrokenAfterSuite.java
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/BrokenAfterSuite.java
diff --git a/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java
new file mode 100644
index 0000000..2fb9ea4
--- /dev/null
+++ b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenMethodDependency.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle;
+
+import org.testng.annotations.*;
+
+public class TestWithBrokenMethodDependency {
+    @Test
+    public void broken() {
+        throw new RuntimeException("broken");
+    }
+
+    @Test(dependsOnMethods = "broken")
+    public void okTest() {
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/test/java/org/gradle/TestWithBrokenSetup.java b/subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenSetup.java
similarity index 100%
rename from subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/src/test/java/org/gradle/TestWithBrokenSetup.java
rename to subprojects/gradle-core/src/integTest/resources/org/gradle/integtests/testng/TestNGIntegrationTest/javaJdk15Failing/src/test/java/org/gradle/TestWithBrokenSetup.java
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/BuildExceptionReporter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/BuildExceptionReporter.java
index 8041fab..8b41a59 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/BuildExceptionReporter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/BuildExceptionReporter.java
@@ -16,15 +16,25 @@
 package org.gradle;
 
 import org.codehaus.groovy.runtime.StackTraceUtils;
+import org.gradle.api.Action;
 import org.gradle.api.GradleException;
 import org.gradle.api.LocationAwareException;
 import org.gradle.api.logging.LogLevel;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
-import org.gradle.util.GUtil;
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.execution.TaskSelectionException;
-import org.slf4j.Logger;
+import org.gradle.initialization.DefaultCommandLineConverter;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.logging.internal.AbstractStyledTextOutput;
+import org.gradle.logging.internal.LoggingCommandLineConverter;
+import org.gradle.util.GUtil;
 
-import java.util.Formatter;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.gradle.logging.StyledTextOutput.Style.Failure;
+import static org.gradle.logging.StyledTextOutput.Style.UserInput;
 
 /**
  * A {@link BuildListener} which reports the build exception, if any.
@@ -34,11 +44,11 @@ public class BuildExceptionReporter extends BuildAdapter {
         None, Sanitized, Full
     }
 
-    private final Logger logger;
+    private final StyledTextOutputFactory textOutputFactory;
     private final StartParameter startParameter;
 
-    public BuildExceptionReporter(Logger logger, StartParameter startParameter) {
-        this.logger = logger;
+    public BuildExceptionReporter(StyledTextOutputFactory textOutputFactory, StartParameter startParameter) {
+        this.textOutputFactory = textOutputFactory;
         this.startParameter = startParameter;
     }
 
@@ -48,6 +58,10 @@ public class BuildExceptionReporter extends BuildAdapter {
             return;
         }
 
+        reportException(failure);
+    }
+
+    public void reportException(Throwable failure) {
         FailureDetails details = new FailureDetails(failure);
         if (failure instanceof GradleException) {
             reportBuildFailure((GradleException) failure, details);
@@ -59,50 +73,65 @@ public class BuildExceptionReporter extends BuildAdapter {
     }
 
     protected void write(FailureDetails details) {
-        Formatter formatter = new Formatter();
-        formatter.format("%nFAILURE: %s", details.summary.toString().trim());
+        StyledTextOutput output = textOutputFactory.create(BuildExceptionReporter.class, LogLevel.ERROR);
+
+        output.println();
+        output.withStyle(Failure).text("FAILURE: ");
+        details.summary.replay(output.withStyle(Failure));
 
-        String location = details.location.toString().trim();
-        if (location.length() > 0) {
-            formatter.format("%n%n* Where:%n%s", location);
+        if (details.location.hasContent) {
+            output.println().println();
+            output.println("* Where:");
+            details.location.replay(output);
         }
 
-        String failureDetails = details.details.toString().trim();
-        if (failureDetails.length() > 0) {
-            formatter.format("%n%n* What went wrong:%n%s", failureDetails);
+        if (details.details.hasContent) {
+            output.println().println();
+            output.println("* What went wrong:");
+            details.details.replay(output);
         }
 
-        String resolution = details.resolution.toString().trim();
-        if (resolution.length() > 0) {
-            formatter.format("%n%n* Try:%n%s", resolution);
+        if (details.resolution.hasContent) {
+            output.println().println();
+            output.println("* Try:");
+            details.resolution.replay(output);
         }
+
+        Throwable exception = null;
         switch (details.exception) {
             case None:
-                logger.error(formatter.toString());
                 break;
             case Sanitized:
-                formatter.format("%n%n* Exception is:");
-                logger.error(formatter.toString(), StackTraceUtils.deepSanitize(details.failure));
+                exception = StackTraceUtils.deepSanitize(details.failure);
                 break;
             case Full:
-                formatter.format("%n%n* Exception is:");
-                logger.error(formatter.toString(), details.failure);
+                exception = details.failure;
                 break;
         }
+
+        if (exception != null) {
+            output.println().println();
+            output.println("* Exception is:");
+            output.exception(exception);
+        }
+
+        output.println();
     }
 
     public void reportInternalError(FailureDetails details) {
-        details.summary.format("Build aborted because of an internal error.");
-        details.details.format("Build aborted because of an unexpected internal error. Please file an issue at: www.gradle.org.");
-        details.resolution.format("Run with -%s option to get additional debug info.",
-                DefaultCommandLine2StartParameterConverter.DEBUG);
+        details.summary.text("Build aborted because of an internal error.");
+        details.details.text("Build aborted because of an unexpected internal error. Please file an issue at: http://www.gradle.org.");
+        details.resolution.text("Run with ");
+        details.resolution.withStyle(UserInput).format("-%s", LoggingCommandLineConverter.DEBUG);
+        details.resolution.text(" option to get additional debug info.");
         details.exception = ExceptionStyle.Full;
     }
 
     private void reportBuildFailure(GradleException failure, FailureDetails details) {
+        boolean debug = startParameter.getLogLevel() == LogLevel.DEBUG;
         boolean stacktrace = startParameter != null
                 && (startParameter.getShowStacktrace() != StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS
-                        || startParameter.getLogLevel() == LogLevel.DEBUG);
+                        || debug);
         if (stacktrace) {
             details.exception = ExceptionStyle.Sanitized;
         }
@@ -115,41 +144,53 @@ public class BuildExceptionReporter extends BuildAdapter {
         if (failure instanceof TaskSelectionException) {
             formatTaskSelectionFailure((TaskSelectionException) failure, details);
         } else {
-            formatGenericFailure(failure, stacktrace, fullStacktrace, details);
+            formatGenericFailure(failure, debug, stacktrace, fullStacktrace, details);
         }
     }
 
     private void formatTaskSelectionFailure(TaskSelectionException failure, FailureDetails details) {
         assert failure.getCause() == null;
-        details.summary.format("Could not determine which tasks to execute.");
-        details.details.format("%s", getMessage(failure));
-        details.resolution.format("Run with -%s to get a list of available tasks.", DefaultCommandLine2StartParameterConverter.TASKS);
+        details.summary.text("Could not determine which tasks to execute.");
+        details.details.text(getMessage(failure));
+        details.resolution.text("Run ");
+        new GradleLauncherMetaData().describeCommand(details.resolution.withStyle(UserInput), ImplicitTasksConfigurer.TASKS_TASK);
+        details.resolution.text(" to get a list of available tasks.");
     }
 
-    private void formatGenericFailure(GradleException failure, boolean stacktrace, boolean fullStacktrace,
+    private void formatGenericFailure(GradleException failure, boolean debug, boolean stacktrace, boolean fullStacktrace,
                                       FailureDetails details) {
-        details.summary.format("Build failed with an exception.");
-        if (!fullStacktrace) {
-            if (!stacktrace) {
-                details.resolution.format("Run with -%s or -%s option to get more details. ",
-                        DefaultCommandLine2StartParameterConverter.STACKTRACE,
-                        DefaultCommandLine2StartParameterConverter.DEBUG);
+        details.summary.text("Build failed with an exception.");
+        if (!debug) {
+            if (!stacktrace && !fullStacktrace) {
+                details.resolution.text("Run with ");
+                details.resolution.withStyle(UserInput).format("-%s", DefaultCommandLineConverter.STACKTRACE);
+                details.resolution.text(" or ");
+                details.resolution.withStyle(UserInput).format("-%s", LoggingCommandLineConverter.DEBUG);
+                details.resolution.text(" option to get more details. ");
+            }
+            else {
+                details.resolution.text("Run with ");
+                details.resolution.withStyle(UserInput).format("-%s", LoggingCommandLineConverter.DEBUG);
+                details.resolution.text(" option to get more details. ");
+            }
+            if (!fullStacktrace) {
+                details.resolution.text("Run with ");
+                details.resolution.withStyle(UserInput).format("-%s", DefaultCommandLineConverter.FULL_STACKTRACE);
+                details.resolution.text(" option to get the full (very verbose) stacktrace.");
             }
-            details.resolution.format("Run with -%s option to get the full (very verbose) stacktrace.",
-                    DefaultCommandLine2StartParameterConverter.FULL_STACKTRACE);
         }
 
         if (failure instanceof LocationAwareException) {
             LocationAwareException scriptException = (LocationAwareException) failure;
             if (scriptException.getLocation() != null) {
-                details.location.format("%s", scriptException.getLocation());
+                details.location.text(scriptException.getLocation());
             }
-            details.details.format("%s", scriptException.getOriginalMessage());
+            details.details.text(scriptException.getOriginalMessage());
             for (Throwable cause : scriptException.getReportableCauses()) {
                 details.details.format("%nCause: %s", getMessage(cause));
             }
         } else {
-            details.details.format("%s", getMessage(failure));
+            details.details.text(getMessage(failure));
         }
     }
 
@@ -163,14 +204,59 @@ public class BuildExceptionReporter extends BuildAdapter {
 
     private static class FailureDetails {
         private ExceptionStyle exception = ExceptionStyle.None;
-        private final Formatter summary = new Formatter();
-        private final Formatter details = new Formatter();
-        private final Formatter location = new Formatter();
-        private final Formatter resolution = new Formatter();
+        private final RecordingStyledTextOutput summary = new RecordingStyledTextOutput();
+        private final RecordingStyledTextOutput details = new RecordingStyledTextOutput();
+        private final RecordingStyledTextOutput location = new RecordingStyledTextOutput();
+        private final RecordingStyledTextOutput resolution = new RecordingStyledTextOutput();
         private final Throwable failure;
 
         public FailureDetails(Throwable failure) {
             this.failure = failure;
         }
     }
+
+    private static class RecordingStyledTextOutput extends AbstractStyledTextOutput {
+        private final List<Action<StyledTextOutput>> events = new ArrayList<Action<StyledTextOutput>>();
+        private boolean hasContent;
+
+        void replay(StyledTextOutput output) {
+            for (Action<StyledTextOutput> event : events) {
+                event.execute(output);
+            }
+            events.clear();
+        }
+
+        @Override
+        protected void doStyleChange(final Style style) {
+            if (!events.isEmpty() && (events.get(events.size() - 1) instanceof ChangeStyleAction)) {
+                events.remove(events.size() - 1);
+            }
+            events.add(new ChangeStyleAction(style));
+        }
+
+        @Override
+        protected void doAppend(final String text) {
+            if (text.length() == 0) {
+                return;
+            }
+            hasContent = true;
+            events.add(new Action<StyledTextOutput>() {
+                public void execute(StyledTextOutput styledTextOutput) {
+                    styledTextOutput.text(text);
+                }
+            });
+        }
+
+        private static class ChangeStyleAction implements Action<StyledTextOutput> {
+            private final Style style;
+
+            public ChangeStyleAction(Style style) {
+                this.style = style;
+            }
+
+            public void execute(StyledTextOutput styledTextOutput) {
+                styledTextOutput.style(style);
+            }
+        }
+    }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/BuildLogger.java b/subprojects/gradle-core/src/main/groovy/org/gradle/BuildLogger.java
index 0f5c290..38c90d1 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/BuildLogger.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/BuildLogger.java
@@ -22,10 +22,11 @@ import org.gradle.api.internal.SettingsInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.logging.Logger;
+import org.gradle.logging.StyledTextOutputFactory;
 import org.gradle.util.Clock;
 
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A {@link org.gradle.BuildListener} which logs the build progress.
@@ -34,10 +35,10 @@ public class BuildLogger implements BuildListener, TaskExecutionGraphListener {
     private final Logger logger;
     private final List<BuildListener> resultLoggers = new ArrayList<BuildListener>();
 
-    public BuildLogger(Logger logger, Clock buildTimeClock, StartParameter startParameter) {
+    public BuildLogger(Logger logger, StyledTextOutputFactory textOutputFactory, Clock buildTimeClock, StartParameter startParameter) {
         this.logger = logger;
-        resultLoggers.add(new BuildExceptionReporter(logger, startParameter));
-        resultLoggers.add(new BuildResultLogger(logger, buildTimeClock));
+        resultLoggers.add(new BuildExceptionReporter(textOutputFactory, startParameter));
+        resultLoggers.add(new BuildResultLogger(textOutputFactory, buildTimeClock));
     }
 
     public void buildStarted(Gradle gradle) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/BuildResultLogger.java b/subprojects/gradle-core/src/main/groovy/org/gradle/BuildResultLogger.java
index c6fe485..96509c3 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/BuildResultLogger.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/BuildResultLogger.java
@@ -15,27 +15,36 @@
  */
 package org.gradle;
 
-import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
 import org.gradle.util.Clock;
 
+import static org.gradle.logging.StyledTextOutput.Style.Failure;
+import static org.gradle.logging.StyledTextOutput.Style.Success;
+
 /**
  * A {@link BuildListener} which logs the final result of the build.
  */
 public class BuildResultLogger extends BuildAdapter {
-    private final Logger logger;
+    private final StyledTextOutputFactory textOutputFactory;
     private final Clock buildTimeClock;
 
-    public BuildResultLogger(Logger logger, Clock buildTimeClock) {
-        this.logger = logger;
+    public BuildResultLogger(StyledTextOutputFactory textOutputFactory, Clock buildTimeClock) {
+        this.textOutputFactory = textOutputFactory;
         this.buildTimeClock = buildTimeClock;
     }
 
     public void buildFinished(BuildResult result) {
+        StyledTextOutput textOutput = textOutputFactory.create(BuildResultLogger.class, LogLevel.LIFECYCLE);
+        textOutput.println();
         if (result.getFailure() == null) {
-            logger.lifecycle(String.format("%nBUILD SUCCESSFUL%n"));
+            textOutput.withStyle(Success).text("BUILD SUCCESSFUL");
         } else {
-            logger.error(String.format("%nBUILD FAILED%n"));
+            textOutput.withStyle(Failure).text("BUILD FAILED");
         }
-        logger.lifecycle(String.format("Total time: %s", buildTimeClock.getTime()));
+        textOutput.println();
+        textOutput.println();
+        textOutput.formatln("Total time: %s", buildTimeClock.getTime());
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/CommandLineArgumentException.java b/subprojects/gradle-core/src/main/groovy/org/gradle/CommandLineArgumentException.java
index 94eb752..6138a51 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/CommandLineArgumentException.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/CommandLineArgumentException.java
@@ -18,6 +18,8 @@ package org.gradle;
 import org.gradle.api.GradleException;
 
 /**
+ * A {@code CommandLineArgumentException} is thrown when command-line arguments cannot be parsed.
+ * 
  * @author Hans Dockter
  */
 public class CommandLineArgumentException extends GradleException {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncher.java b/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncher.java
index f88c285..2d31632 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncher.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncher.java
@@ -45,7 +45,7 @@ import org.gradle.initialization.DefaultGradleLauncherFactory;
  */
 public abstract class GradleLauncher {
 
-    private static GradleLauncherFactory factory = new DefaultGradleLauncherFactory();
+    private static GradleLauncherFactory factory;
 
     /**
      * <p>Executes the build for this GradleLauncher instance and returns the result. Note that when the build fails,
@@ -79,7 +79,14 @@ public abstract class GradleLauncher {
      * @return The GradleLauncher. Never returns null.
      */
     public static GradleLauncher newInstance(final StartParameter startParameter) {
-        return factory.newInstance(startParameter);
+        return getFactory().newInstance(startParameter);
+    }
+
+    private static synchronized GradleLauncherFactory getFactory() {
+        if (factory == null) {
+            factory = new DefaultGradleLauncherFactory();
+        }
+        return factory;
     }
 
     /**
@@ -91,7 +98,7 @@ public abstract class GradleLauncher {
      * @return The GradleLauncher. Never returns null.
      */
     public static GradleLauncher newInstance(final String... commandLineArgs) {
-        return factory.newInstance(commandLineArgs);
+        return getFactory().newInstance(commandLineArgs);
     }
 
     /**
@@ -102,11 +109,11 @@ public abstract class GradleLauncher {
      * @return The GradleLauncher. Never returns null.
      */
     public static StartParameter createStartParameter(final String... commandLineArgs) {
-        return factory.createStartParameter(commandLineArgs);
+        return getFactory().createStartParameter(commandLineArgs);
     }
 
-    public static void injectCustomFactory(GradleLauncherFactory gradleLauncherFactory) {
-        factory = gradleLauncherFactory == null ? new DefaultGradleLauncherFactory() : gradleLauncherFactory;
+    public static synchronized void injectCustomFactory(GradleLauncherFactory gradleLauncherFactory) {
+        factory = gradleLauncherFactory;
     }
 
     /**
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncherFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncherFactory.java
index be98f25..33462e0 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncherFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/GradleLauncherFactory.java
@@ -30,7 +30,7 @@ public interface GradleLauncherFactory {
      */
     GradleLauncher newInstance(StartParameter startParameter);
 
-    GradleLauncher newInstance(final String[] commandLineArgs);
+    GradleLauncher newInstance(String... commandLineArgs);
 
-    StartParameter createStartParameter(String[] commandLineArgs);
+    StartParameter createStartParameter(String... commandLineArgs);
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/StartParameter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/StartParameter.java
index e9d69db..d16f481 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/StartParameter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/StartParameter.java
@@ -18,7 +18,7 @@ package org.gradle;
 
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.execution.*;
 import org.gradle.groovy.scripts.UriScriptSource;
@@ -44,6 +44,15 @@ import java.util.*;
  * @see GradleLauncher
  */
 public class StartParameter {
+    public static final String GRADLE_USER_HOME_PROPERTY_KEY = "gradle.user.home";
+    /**
+     * The default user home directory.
+     */
+    public static final File DEFAULT_GRADLE_USER_HOME = new File(System.getProperty("user.home") + "/.gradle");
+
+    /**
+     * Specifies the detail to include in stacktraces.
+     */
     public enum ShowStacktrace {
         INTERNAL_EXCEPTIONS, ALWAYS, ALWAYS_FULL
     }
@@ -56,11 +65,6 @@ public class StartParameter {
     private boolean searchUpwards = true;
     private Map<String, String> projectProperties = new HashMap<String, String>();
     private Map<String, String> systemPropertiesArgs = new HashMap<String, String>();
-    public static final String GRADLE_USER_HOME_PROPERTY_KEY = "gradle.user.home";
-    /**
-     * The default user home directory.
-     */
-    public static final File DEFAULT_GRADLE_USER_HOME = new File(System.getProperty("user.home") + "/.gradle");
     private File gradleUserHomeDir = new File(GUtil.elvis(System.getProperty(GRADLE_USER_HOME_PROPERTY_KEY), DEFAULT_GRADLE_USER_HOME.getAbsolutePath()));
     private CacheUsage cacheUsage = CacheUsage.ON;
     private ScriptSource buildScriptSource;
@@ -71,11 +75,10 @@ public class StartParameter {
     private ShowStacktrace showStacktrace = ShowStacktrace.INTERNAL_EXCEPTIONS;
     private File buildFile;
     private List<File> initScripts = new ArrayList<File>();
-    private boolean showHelp;
-    private boolean showVersion;
-    private boolean launchGUI;
     private boolean dryRun;
     private boolean noOpt;
+    private boolean colorOutput = true;
+    private boolean profile;
 
     /**
      * Creates a {@code StartParameter} with default values. This is roughly equivalent to running Gradle on the
@@ -107,11 +110,11 @@ public class StartParameter {
         startParameter.buildExecuter = buildExecuter;
         startParameter.defaultProjectSelector = defaultProjectSelector;
         startParameter.logLevel = logLevel;
+        startParameter.colorOutput = colorOutput;
         startParameter.showStacktrace = showStacktrace;
-        startParameter.showHelp = showHelp;
-        startParameter.showVersion = showVersion;
         startParameter.dryRun = dryRun;
         startParameter.noOpt = noOpt;
+        startParameter.profile = profile;
         return startParameter;
     }
 
@@ -127,6 +130,8 @@ public class StartParameter {
         startParameter.gradleUserHomeDir = gradleUserHomeDir;
         startParameter.cacheUsage = cacheUsage;
         startParameter.logLevel = logLevel;
+        startParameter.colorOutput = colorOutput;
+        startParameter.profile = profile;
         return startParameter;
     }
 
@@ -371,22 +376,6 @@ public class StartParameter {
         this.cacheUsage = cacheUsage;
     }
 
-    public boolean isShowHelp() {
-        return showHelp;
-    }
-
-    public void setShowHelp(boolean showHelp) {
-        this.showHelp = showHelp;
-    }
-
-    public boolean isShowVersion() {
-        return showVersion;
-    }
-
-    public void setShowVersion(boolean showVersion) {
-        this.showVersion = showVersion;
-    }
-
     public boolean isDryRun() {
         return dryRun;
     }
@@ -494,14 +483,38 @@ public class StartParameter {
     }
 
     /**
-       Determines whether or not the GUI was requested to be launched.
-    */
-    public boolean isLaunchGUI() {
-        return launchGUI;
+     * Returns true if logging output should be displayed in color when Gradle is running in a terminal which supports
+     * color output. The default value is true.
+     *
+     * @return true if logging output should be displayed in color.
+     */
+    public boolean isColorOutput() {
+        return colorOutput;
     }
 
-    public void setLaunchGUI(boolean launchGUI) {
-        this.launchGUI = launchGUI;
+    /**
+     * Specifies whether logging output should be displayed in color.
+     *
+     * @param colorOutput true if logging output should be displayed in color.
+     */
+    public void setColorOutput(boolean colorOutput) {
+        this.colorOutput = colorOutput;
+    }
+
+    /**
+     * Specifies if a profile report should be generated.
+     * @param profile true if a profile report should be generated
+     */
+    public void setProfile(boolean profile) {
+        this.profile = profile;
+    }
+
+    /**
+     * Returns true if a profile report will be generated.
+     * @return
+     */
+    public boolean isProfile() {
+        return profile;
     }
 
     @Override
@@ -523,11 +536,9 @@ public class StartParameter {
                 ", showStacktrace=" + showStacktrace +
                 ", buildFile=" + buildFile +
                 ", initScripts=" + initScripts +
-                ", showHelp=" + showHelp +
-                ", showVersion=" + showVersion +
-                ", launchGUI=" + launchGUI +
                 ", dryRun=" + dryRun +
                 ", noOpt=" + noOpt +
+                ", profile=" + profile +
                 '}';
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/TaskExecutionLogger.java b/subprojects/gradle-core/src/main/groovy/org/gradle/TaskExecutionLogger.java
index ced9f87..2d6a745 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/TaskExecutionLogger.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/TaskExecutionLogger.java
@@ -37,7 +37,7 @@ public class TaskExecutionLogger implements TaskExecutionListener {
 
     public void beforeExecute(Task task) {
         assert currentTask == null;
-        currentTask = progressLoggerFactory.start(getDisplayName(task));
+        currentTask = progressLoggerFactory.start(TaskExecutionLogger.class.getName(), getDisplayName(task));
         currentTask.progress(getDisplayName(task));
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Action.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Action.java
index bd6a20c..e8874cd 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Action.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Action.java
@@ -17,10 +17,12 @@ package org.gradle.api;
 
 /**
  * Performs some action against objects of type T.
+ *
+ * @param <T> The type of object which this action accepts. 
  */
 public interface Action<T> {
     /**
-     * Performs this action against the given object
+     * Performs this action against the given object.
      *
      * @param t The object to perform the action on.
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/DefaultTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/DefaultTask.java
index 041a87f..ee8c52c 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/DefaultTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/DefaultTask.java
@@ -27,7 +27,7 @@ import org.gradle.api.internal.NoConventionMapping;
  * @author Hans Dockter
  */
 @NoConventionMapping
-public class DefaultTask extends AbstractTask implements Task {
+public class DefaultTask extends AbstractTask {
     public DefaultTask() {
         if (this instanceof GroovyObject) {
             GroovyObject groovyObject = (GroovyObject) this;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/LocationAwareException.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/LocationAwareException.java
index 6b80c6c..d6dbbb4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/LocationAwareException.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/LocationAwareException.java
@@ -20,7 +20,8 @@ import org.gradle.groovy.scripts.ScriptSource;
 import java.util.List;
 
 /**
- * A {@code LocationAwareException} is an exception which can be annotated with a location in a script.
+ * A {@code LocationAwareException} is an exception which can be annotated with a location in a script. Note that
+ * most implementations of this interface are generated dynamically by an {@link org.gradle.api.internal.ExceptionAnalyser}.
  */
 public interface LocationAwareException {
     /**
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Plugin.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Plugin.java
index 878ed7a..67e207f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Plugin.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Plugin.java
@@ -21,6 +21,7 @@ package org.gradle.api;
  * objects.</p>
  *
  * @author Hans Dockter
+ * @param <T> The type of object which this plugin can configure. 
  */
 public interface Plugin<T> {
     /**
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Project.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Project.java
index 9342e29..f17e00b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Project.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Project.java
@@ -170,7 +170,7 @@ public interface Project extends Comparable<Project> {
     public static final String DEFAULT_BUILD_FILE = "build.gradle";
 
     /**
-     * The hierarchy separator for project and task path names
+     * The hierarchy separator for project and task path names.
      */
     public static final String PATH_SEPARATOR = ":";
 
@@ -218,8 +218,7 @@ public interface Project extends Comparable<Project> {
     /**
      * <p>Sets the build directory of this project. The build directory is the directory which all artifacts are
      * generated into. The path parameter is evaluated as described for {@link #file(Object)}. This mean you can use,
-     * amongst other things, a relative or absolute path or File object to specify the build directory.
-     * </p>
+     * amongst other things, a relative or absolute path or File object to specify the build directory. </p>
      *
      * @param path The build directory. This is evaluated as for {@link #file(Object)}
      */
@@ -271,6 +270,20 @@ public interface Project extends Comparable<Project> {
     String getName();
 
     /**
+     * Returns the description of this project.
+     *
+     * @return the description. May return null.
+     */
+    String getDescription();
+
+    /**
+     * Sets a description for this project.
+     *
+     * @param description The description of the project. Might be null.
+     */
+    void setDescription(String description);
+
+    /**
      * <p>Returns the group of this project. Gradle always uses the toString() value of a group. The group defaults to
      * the path with dots a separators.</p> <p/> <p>You can access this property in your build file using
      * <code>group</code></p>
@@ -330,10 +343,9 @@ public interface Project extends Comparable<Project> {
 
     /**
      * <p>Returns this project. This method is useful in build files to explicitly access project properties and
-     * methods. For example, using <code>project.name</code> can express your intent better than using <code>name</code>.
-     * This method also allows you to access project properties from a scope where the property may be hidden,
-     * such as, for example, from a method or closure.
-     * </p>
+     * methods. For example, using <code>project.name</code> can express your intent better than using
+     * <code>name</code>. This method also allows you to access project properties from a scope where the property may
+     * be hidden, such as, for example, from a method or closure. </p>
      *
      * <p>You can access this property in your build file using <code>project</code></p>
      *
@@ -670,7 +682,7 @@ public interface Project extends Comparable<Project> {
     Map<Project, Set<Task>> getAllTasks(boolean recursive);
 
     /**
-     * Returns the set of tasks with the given name contained in this project, and optionally its subprojects.</p>
+     * <p>Returns the set of tasks with the given name contained in this project, and optionally its subprojects.</p>
      *
      * @param name The name of the task to locate.
      * @param recursive If true, returns the tasks of this project and its subprojects. If false, returns the tasks of
@@ -901,7 +913,6 @@ public interface Project extends Comparable<Project> {
      * Executes a Java main class. The closure configures a {@link org.gradle.process.JavaExecSpec}.
      *
      * @param closure The closure for configuring the execution.
-     *
      * @return the result of the execution
      */
     ExecResult javaexec(Closure closure);
@@ -910,7 +921,6 @@ public interface Project extends Comparable<Project> {
      * Executes an external command. The closure configures a {@link org.gradle.process.ExecSpec}.
      *
      * @param closure The closure for configuring the execution.
-     *
      * @return the result of the execution
      */
     ExecResult exec(Closure closure);
@@ -920,10 +930,28 @@ public interface Project extends Comparable<Project> {
      *
      * @param path The path to convert.
      * @return The absolute path.
+     * @deprecated Use {@link #absoluteProjectPath(String)} instead.
      */
+    @Deprecated
     String absolutePath(String path);
 
     /**
+     * <p>Converts a name to an absolute project path, resolving names relative to this project.</p>
+     *
+     * @param path The path to convert.
+     * @return The absolute path.
+     */
+    String absoluteProjectPath(String path);
+
+    /**
+     * <p>Converts a name to a project path relative to this project.</p>
+     *
+     * @param path The path to convert.
+     * @return The relative path.
+     */
+    String relativeProjectPath(String path);
+
+    /**
      * <p>Returns the <code>AntBuilder</code> for this project. You can use this in your build file to execute ant
      * tasks.</p> <p/> <p>You can access this property in your build file using <code>ant</code></p>
      *
@@ -1096,13 +1124,25 @@ public interface Project extends Comparable<Project> {
     Map<String, ?> getProperties();
 
     /**
-     * Returns the value of the given property.  This method locates a property as follows:</p> <p/> <ol> <p/> <li>If
-     * this project object has a property with the given name, return the value of the property.</li> <p/> <li>If this
-     * project's convention object has a property with the given name, return the value of the property.</li> <p/>
-     * <li>If this project has an additional property with the given name, return the value of the property.</li> <p/>
-     * <li>If this project has a task with the given name, return the task.</li> <p/> <li>Search up through this
-     * project's ancestor projects for a convention property or additional property with the given name.</li> <p/>
-     * <li>If not found, throw {@link MissingPropertyException}</li> <p/> </ol>
+     * <p>Returns the value of the given property.  This method locates a property as follows:</p>
+     *
+     * <ol>
+     *
+     * <li>If this project object has a property with the given name, return the value of the property.</li>
+     *
+     * <li>If this project's convention object has a property with the given name, return the value of the
+     * property.</li>
+     *
+     * <li>If this project has an additional property with the given name, return the value of the property.</li>
+     *
+     * <li>If this project has a task with the given name, return the task.</li>
+     *
+     * <li>Search up through this project's ancestor projects for a convention property or additional property with the
+     * given name.</li>
+     *
+     * <li>If not found, throw {@link MissingPropertyException}</li>
+     *
+     * </ol>
      *
      * @param propertyName The name of the property.
      * @return The value of the property, possibly null.
@@ -1128,8 +1168,8 @@ public interface Project extends Comparable<Project> {
 
     /**
      * Returns the {@link org.gradle.api.logging.LoggingManager} which can be used to control the logging level and
-     * standard output/error capture for this project's build script. By default, System.out is redirected to the
-     * Gradle logging system at the QUIET log level, and System.err is redirected at the ERROR log level.
+     * standard output/error capture for this project's build script. By default, System.out is redirected to the Gradle
+     * logging system at the QUIET log level, and System.err is redirected at the ERROR log level.
      *
      * @return the LoggingManager. Never returns null.
      */
@@ -1147,7 +1187,7 @@ public interface Project extends Comparable<Project> {
      * <p>Starts redirection of standard output during to the logging system during project evaluation. By default
      * redirection is enabled and the output is redirected to the QUIET level. System.err is always redirected to the
      * ERROR level. Redirection of output at execution time can be configured via the tasks.</p>
-     * 
+     *
      * <p>In a multi-project this is a per-project setting.</p>
      *
      * @param level The level standard out should be logged to.
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java
index 785ae68..f91cdff 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/ProjectEvaluationListener.java
@@ -31,7 +31,7 @@ public interface ProjectEvaluationListener {
     void beforeEvaluate(Project project);
 
     /**
-     * This method is called when a project has been evaluated, and before the evaluated project is made available to
+     * <p>This method is called when a project has been evaluated, and before the evaluated project is made available to
      * other projects.</p>
      *
      * @param project The project which was evaluated. Never null.
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Task.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Task.java
index 2ee74cf..0a083bd 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Task.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Task.java
@@ -63,7 +63,7 @@ import java.util.Set;
  * the task as parameter.  You can add action closures to a task by calling {@link #doFirst(groovy.lang.Closure)} or
  * {@link #doLast(groovy.lang.Closure)}  or using the left-shift << operator.</p>
  *
- * There are 2 special exceptions which a task action can throw to abort execution and continue without failing the
+ * <p>There are 2 special exceptions which a task action can throw to abort execution and continue without failing the
  * build. A task action can abort execution of the action and continue to the next action of the task by throwing a
  * {@link org.gradle.api.tasks.StopActionException}. A task action can abort execution of the task and continue to the
  * next task by throwing a {@link org.gradle.api.tasks.StopExecutionException}. Using these exceptions allows you to
@@ -143,7 +143,7 @@ public interface Task extends Comparable<Task> {
     public static final String TASK_ACTION = "action";
 
     /**
-     * </p>Returns the name of this task. The name uniquely identifies the task within its {@link Project}.</p>
+     * <p>Returns the name of this task. The name uniquely identifies the task within its {@link Project}.</p>
      *
      * @return The name of the task. Never returns null.
      */
@@ -263,6 +263,14 @@ public interface Task extends Comparable<Task> {
     TaskState getState();
 
     /**
+     * Sets whether the task actually did any work.  Most built-in tasks will set this automatically, but
+     * it may be useful to manually indicate this for custom user tasks.
+     * <p>This is useful when combined with onlyIf { dependsOnTaskDidWork() }.
+     * @param didWork indicates if the task did any work
+     */
+    void setDidWork(boolean didWork);
+
+    /**
      * <p>Checks if the task actually did any work.  Even if a Task executes, it may determine that it has nothing to
      * do.  For example, the Compile task may determine that source files have not changed since the last time a the
      * task was run.</p>
@@ -400,7 +408,7 @@ public interface Task extends Comparable<Task> {
     Task captureStandardOutput(LogLevel level);
 
     /**
-     * Returns the value of the given property of this task.  This method locates a property as follows:</p>
+     * <p>Returns the value of the given property of this task.  This method locates a property as follows:</p>
      *
      * <ol>
      *
@@ -460,15 +468,15 @@ public interface Task extends Comparable<Task> {
     Convention getConvention();
 
     /**
-     * Returns the description of a task.
+     * Returns the description of this task.
      *
-     * @see #setDescription(String)
+     * @return the description. May return null.
      */
     String getDescription();
 
     /**
-     * Adds a text to describe what the task does to the user of the build. The description will be displayed when
-     * <code>gradle -t</code> is called.
+     * Sets a description for this task. This should describe what the task does to the user of the build. The
+     * description will be displayed when <code>gradle tasks</code> is called.
      *
      * @param description The description of the task. Might be null.
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Transformer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Transformer.java
index 3795be4..0956c58 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/Transformer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/Transformer.java
@@ -17,6 +17,8 @@ package org.gradle.api;
 
 /**
  * <p>A {@code Transformer} transforms objects of type T.</p>
+ *
+ * @param <T> The type of object which this transformer can transform.
  */
 public interface Transformer<T> {
     /**
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/Configuration.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
index 3a15345..c89237f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/Configuration.java
@@ -291,7 +291,7 @@ public interface Configuration extends FileCollection {
     <T extends Dependency> Set<T> getAllDependencies(Class<T> type);
 
     /**
-     * Adds a dependency to this configuration
+     * Adds a dependency to this configuration.
      *
      * @param dependency The dependency to be added.
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/IvyObjectBuilder.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/IvyObjectBuilder.java
deleted file mode 100644
index 4b8a1c3..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/IvyObjectBuilder.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.artifacts;
-
-import groovy.lang.Closure;
-import org.gradle.api.Transformer;
-
-/**
- * <p>A {@code IvyObjectBuilder} builds Ivy domain objects of type {@code T}. You can influence the construction of the
- * Ivy objects by adding transformers to this builder. A transformer can either be a closure, or a {@link Transformer}
- * implementation. The transformers are called in the order added.</p>
- */
-public interface IvyObjectBuilder<T> {
-    /**
-     * <p>Adds a transformer to this builder.</p>
-     *
-     * @param transformer The transformer to add.
-     */
-    void addIvyTransformer(Transformer<T> transformer);
-
-    /**
-     * <p>Adds a transformation closure to this builder. The closure is passed the object to transform as a parameter.
-     * The closure can return an object of type T, which will be used as the transformed value. The object to transform
-     * is also set as the delegate of the closure.</p>
-     *
-     * @param transformer The transformation closure to add.
-     */
-    void addIvyTransformer(Closure transformer);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependenciesBuildInstruction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependenciesBuildInstruction.java
deleted file mode 100644
index bbb4b06..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependenciesBuildInstruction.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts;
-
-import java.util.List;
-import java.util.Collections;
-
-/**
- * @author Hans Dockter
- */
-public class ProjectDependenciesBuildInstruction {
-    private List<String> taskNames;
-
-    public ProjectDependenciesBuildInstruction(List<String> taskNames) {
-        this.taskNames = taskNames;
-    }
-
-    public List<String> getTaskNames() {
-        if (taskNames == null) {
-            return Collections.emptyList();
-        }
-        return taskNames;
-    }
-
-    public boolean isRebuild() {
-        return taskNames != null;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        ProjectDependenciesBuildInstruction that = (ProjectDependenciesBuildInstruction) o;
-
-        if (taskNames != null ? !taskNames.equals(that.taskNames) : that.taskNames != null) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return taskNames != null ? taskNames.hashCode() : 0;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java
index aff0211..364da14 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ProjectDependency.java
@@ -24,7 +24,7 @@ import org.gradle.api.Project;
  */
 public interface ProjectDependency extends ModuleDependency, SelfResolvingDependency {
     /**
-     * Returns the project associated with this project dependency
+     * Returns the project associated with this project dependency.
      */
     Project getDependencyProject();
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java
index 150c753..ff03b62 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedArtifact.java
@@ -18,6 +18,8 @@ package org.gradle.api.artifacts;
 import java.io.File;
 
 /**
+ * Information about a resolved artifact.
+ * 
  * @author Hans Dockter
  */
 public interface ResolvedArtifact {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java
index ff40422..46545f7 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/ResolvedDependency.java
@@ -18,16 +18,18 @@ package org.gradle.api.artifacts;
 import java.util.Set;
 
 /**
+ * Information about a resolved dependency.
+ *
  * @author Hans Dockter
  */
 public interface ResolvedDependency {
     /**
-     * Returns the name of the resolved dependency
+     * Returns the name of the resolved dependency.
      */
     String getName();
 
     /**
-     * Returns the module group of the resolved dependency
+     * Returns the module group of the resolved dependency.
      */
     String getModuleGroup();
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java
index 89a76db..dcb3d56 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/DependencyHandler.java
@@ -48,9 +48,14 @@ import java.util.Map;
  * way, a {@link Dependency} object is created. You can use this object to query or further configure the
  * dependency.</p>
  *
+ * <p>You can also always add instances of
+ * {@link org.gradle.api.artifacts.Dependency} directly:</p>
+ *
+ * <code><i>configurationName</i> <instance></code>
+ *
  * <h2>External Modules</h2>
  *
- * There are 3 notations supported for declaring a dependency on an external module. One is a string notation:</p>
+ * <p>There are 2 notations supported for declaring a dependency on an external module. One is a string notation:</p>
  *
  * <code><i>configurationName</i> "<i>group</i>:<i>name</i>:<i>version</i>:<i>classifier</i>"</code>
  *
@@ -59,7 +64,9 @@ import java.util.Map;
  * <code><i>configurationName</i> group: <i>group</i>:, name: <i>name</i>, version: <i>version</i>, classifier:
  * <i>classifier</i></code>
  *
- * <p>In both notations, all properties, except name, are optional. External dependencies are represented using a {@link
+ * <p>In both notations, all properties, except name, are optional.</p>
+ *
+ * <p>External dependencies are represented by a {@link
  * org.gradle.api.artifacts.ExternalModuleDependency}.</p>
  *
  * <h2>Client Modules</h2>
@@ -103,7 +110,7 @@ public interface DependencyHandler {
     Dependency add(String configurationName, Object dependencyNotation);
 
     /**
-     * Adds a dependency to the given configuration, and configures the dependency using the given closure/
+     * Adds a dependency to the given configuration, and configures the dependency using the given closure.
      *
      * @param configurationName The name of the configuration.
      * @param dependencyNotation The dependency notation, in one of the notations described above.
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java
index 088aac7..7570a73 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandler.java
@@ -26,6 +26,8 @@ import org.gradle.api.internal.artifacts.configurations.ResolverProvider;
 import java.util.Map;
 
 /**
+ * A {@code RepositoryHandler} manages a set of repositories, allowing repositories to be defined and queried.
+ *
  * @author Hans Dockter
  */
 public interface RepositoryHandler extends ResolverContainer, ResolverProvider {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandlerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandlerFactory.java
deleted file mode 100644
index ab5f18b..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/dsl/RepositoryHandlerFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts.dsl;
-
-import org.gradle.api.plugins.Convention;
-
-/**
- * @author Hans Dockter
- */
-public interface RepositoryHandlerFactory {
-    /**
-     * Creates a repository handler. The returned handler will also implement {@link
-     * org.gradle.api.internal.IConventionAware}.
-     */
-    RepositoryHandler createRepositoryHandler(Convention convention);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/IndexFileUtil.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/IndexFileUtil.java
deleted file mode 100644
index f0e0b71..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/IndexFileUtil.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.artifacts.indexing;
-
-import java.io.File;
-
-/**
- * @author Tom Eyckmans
- */
-public class IndexFileUtil {
-//    private final File jarsIndexDirectory;
-
-    
-
-    public File packageIndexFile(File jarFile) {
-        return new File(jarFile.getParent(), "." + jarFile.getName() + ".package.index");
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarArtifactIndexer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarArtifactIndexer.java
deleted file mode 100644
index 42227dc..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarArtifactIndexer.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.artifacts.indexing;
-
-import org.gradle.api.GradleException;
-import org.apache.commons.io.IOUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-
-/**
- * @author Tom Eyckmans
- */
-public class JarArtifactIndexer {
-    private IndexFileUtil indexFileUtil;
-
-    public JarArtifactIndexer(IndexFileUtil indexFileUtil) {
-        this.indexFileUtil = indexFileUtil;
-    }
-
-    public File index(File jarFile) {
-        if (jarFile == null) {
-            throw new IllegalArgumentException("jarFile is null!");
-        }
-
-        final String jarFileAbsolutePath = jarFile.getAbsolutePath();
-
-        if (!jarFile.exists()) {
-            throw new IllegalArgumentException("jarFile doesn't exists! (" + jarFileAbsolutePath + ")");
-        }
-        if (!jarFile.isFile()) {
-            throw new IllegalArgumentException("jarFile is not a file! (" + jarFileAbsolutePath + ")");
-        }
-        if (!jarFile.getName().endsWith(".jar")) {
-            throw new IllegalArgumentException("jarFile is not a jarFile! (" + jarFileAbsolutePath + ")");
-        }
-
-        final File packageIndexFile = indexFileUtil.packageIndexFile(jarFile);
-
-        BufferedWriter indexFileWriter = null;
-        try {
-            indexFileWriter = new BufferedWriter(new FileWriter(packageIndexFile));
-
-            final BufferedWriter writer = indexFileWriter;
-
-            new JarFilePackageLister().listJarPackages(jarFile, new JarFilePackageListener() {
-                public void receivePackage(String packageName) {
-                    try {
-                        writer.write(packageName);
-                        writer.newLine();
-                    } catch (IOException e) {
-                        throw new GradleException("failed to write to index file", e);
-                    }
-                }
-            });
-
-            indexFileWriter.flush();
-        } catch (IOException e) {
-            throw new GradleException("failed to index jar file (" + jarFileAbsolutePath + ")", e);
-        } finally {
-            IOUtils.closeQuietly(indexFileWriter);
-        }
-
-        return packageIndexFile;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarFilePackageListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarFilePackageListener.java
deleted file mode 100644
index b1bba3b..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarFilePackageListener.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.artifacts.indexing;
-
-/**
- * @author Tom Eyckmans
- */
-public interface JarFilePackageListener {
-    void receivePackage(String packageName);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarFilePackageLister.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarFilePackageLister.java
deleted file mode 100644
index 0402be2..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/indexing/JarFilePackageLister.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.artifacts.indexing;
-
-import org.gradle.api.GradleException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * @author Tom Eyckmans
- */
-public class JarFilePackageLister {
-    public void listJarPackages(File jarFile, JarFilePackageListener listener) {
-        if (jarFile == null) {
-            throw new IllegalArgumentException("jarFile is null!");
-        }
-
-        final String jarFileAbsolutePath = jarFile.getAbsolutePath();
-
-        if (!jarFile.exists()) {
-            throw new IllegalArgumentException("jarFile doesn't exists! (" + jarFileAbsolutePath + ")");
-        }
-        if (!jarFile.isFile()) {
-            throw new IllegalArgumentException("jarFile is not a file! (" + jarFileAbsolutePath + ")");
-        }
-        if (!jarFile.getName().endsWith(".jar")) {
-            throw new IllegalArgumentException("jarFile is not a jarFile! (" + jarFileAbsolutePath + ")");
-        }
-
-        try {
-            ZipFile zipFile = new ZipFile(jarFile);
-            try {
-                final Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
-
-                while (zipFileEntries.hasMoreElements()) {
-                    final ZipEntry zipFileEntry = zipFileEntries.nextElement();
-
-                    if (zipFileEntry.isDirectory()) {
-                        final String zipFileEntryName = zipFileEntry.getName();
-
-                        if (!zipFileEntryName.startsWith("META-INF")) {
-                            listener.receivePackage(zipFileEntryName);
-                        }
-                    }
-                }
-            } finally {
-                zipFile.close();
-            }
-        } catch (IOException e) {
-            throw new GradleException("failed to scan jar file for packages (" + jarFileAbsolutePath + ")", e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMapping.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMapping.java
index 33a4d8d..566aaf4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMapping.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/Conf2ScopeMapping.java
@@ -30,6 +30,8 @@ public class Conf2ScopeMapping {
     private String scope;
 
     /**
+     * Constructs a {@code Conf2ScopeMapping}.
+     *
      * @param priority The priority of this mapping
      * @param configuration The configuration name
      * @param scope The Maven scope name
@@ -41,14 +43,14 @@ public class Conf2ScopeMapping {
     }
 
     /**
-     * Returns the priority
+     * Returns the priority.
      */
     public Integer getPriority() {
         return priority;
     }
 
     /**
-     * Returns the dependency configuration name
+     * Returns the dependency configuration name.
      */
     public Configuration getConfiguration() {
         return configuration;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/GroovyPomFilterContainer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/GroovyPomFilterContainer.java
deleted file mode 100644
index fc1d7e3..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/GroovyPomFilterContainer.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts.maven;
-
-import groovy.lang.Closure;
-
-/**
- * @author Hans Dockter
- */
-public interface GroovyPomFilterContainer extends PomFilterContainer {
-    /**
-     * Adds a publish filter.
-     *
-     * @param name   The name of the filter
-     * @param filter The filter
-     * @return The Maven pom associated with the closure
-     * @see PublishFilter
-     * @see PomFilterContainer#addFilter(String, org.gradle.api.artifacts.maven.PublishFilter)
-     */
-    MavenPom addFilter(String name, Closure filter);
-
-    /**
-     * Sets the default publish filter.
-     *
-     * @param filter The filter to be set
-     * @see PublishFilter
-     * @see PomFilterContainer#setFilter(org.gradle.api.artifacts.maven.PublishFilter)
-     */
-    void filter(Closure filter);
-
-    /**
-     * Configures a pom by a closure. The closure statements are delegated to the pom object associated with the given name.
-     *
-     * @param name
-     * @param configureClosure
-     * @return The pom object associated with the given name.
-     * @see PomFilterContainer#pom(String)
-     */
-    MavenPom pom(String name, Closure configureClosure);
-
-    /**
-     * Configures the default pom by a closure. The closure statements are delegated to the default pom.
-     *
-     * @param configureClosure
-     * @return The default pom.
-     * @see PomFilterContainer#getPom()
-     */
-    MavenPom pom(Closure configureClosure);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java
index 56ee058..26bc0f2 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenPom.java
@@ -59,56 +59,76 @@ public interface MavenPom {
     MavenPom project(Closure pom);
 
     /**
+     * Returns the group id for this POM.
+     *
      * @see org.apache.maven.model.Model#setGroupId(String)
      */
     String getGroupId();
 
     /**
-     * org.apache.maven.model.Model#getGroupId
+     * Sets the group id for this POM.
+     *
+     * @see org.apache.maven.model.Model#getGroupId
      * @return this
      */
     MavenPom setGroupId(String groupId);
 
     /**
+     * Returns the artifact id for this POM.
+     * 
      * @see org.apache.maven.model.Model#getArtifactId()
      */
     String getArtifactId();
 
     /**
+     * Sets the artifact id for this POM.
+     *
      * @see org.apache.maven.model.Model#setArtifactId(String)
      * @return this
      */
     MavenPom setArtifactId(String artifactId);
 
     /**
+     * Returns the version for this POM.
+     *
      * @see org.apache.maven.model.Model#getVersion()
      */
     String getVersion();
 
     /**
+     * Sets the version for this POM.
+     *
      * @see org.apache.maven.model.Model#setVersion(String)
      * @return this
      */
     MavenPom setVersion(String version);
 
     /**
+     * Returns the packaging for this POM.
+     *
      * @see org.apache.maven.model.Model#getPackaging()
      */
     String getPackaging();
 
     /**
+     * Sets the packaging for this POM.
+     *
      * @see org.apache.maven.model.Model#setPackaging(String)
      * @return this
      */
     MavenPom setPackaging(String packaging);
 
     /**
+     * Sets the dependencies for this POM.
+     *
      * @see org.apache.maven.model.Model#setDependencies(java.util.List)
      * @return this
      */
     MavenPom setDependencies(List<Dependency> dependencies);
 
     /**
+     * Returns the dependencies for this POM.
+     * 
      * @see org.apache.maven.model.Model#getDependencies()
      */
     List<Dependency> getDependencies();
@@ -168,19 +188,19 @@ public interface MavenPom {
     MavenPom whenConfigured(Action<MavenPom> action);
 
     /**
-     * <p>Adds a closure to be called when the pom xml has been created. The xml is passed to the closure as a
-     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The xml might be modified.</p>
+     * <p>Adds a closure to be called when the POM XML has been created. The XML is passed to the closure as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.</p>
      *
-     * @param closure The closure to execute when the pom xml has been created.
+     * @param closure The closure to execute when the POM XML has been created.
      * @return this
      */
     MavenPom withXml(Closure closure);
 
     /**
-     * <p>Adds an action to be called when the pom xml has been created. The xml is passed to the action as a
-     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The xml might be modified.</p>
+     * <p>Adds an action to be called when the POM XML has been created. The XML is passed to the action as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.</p>
      *
-     * @param action The action to execute when the pom xml has been created.
+     * @param action The action to execute when the POM XML has been created.
      * @return this
      */
     MavenPom withXml(Action<XmlProvider> action);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
index 8c4abb4..c6f59fa 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/MavenResolver.java
@@ -19,6 +19,8 @@ import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.apache.maven.settings.Settings;
 
 /**
+ * A {@link org.apache.ivy.plugins.resolver.DependencyResolver} which resolves dependencies from Maven repositories.
+ * 
  * @author Hans Dockter
  */
 public interface MavenResolver extends DependencyResolver, PomFilterContainer {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PomFilterContainer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PomFilterContainer.java
index 62a6af5..91a53b4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PomFilterContainer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PomFilterContainer.java
@@ -15,9 +15,12 @@
  */
 package org.gradle.api.artifacts.maven;
 
+import groovy.lang.Closure;
 import org.gradle.api.internal.artifacts.publish.maven.deploy.PomFilter;
 
 /**
+ * Manages a set of {@link MavenPom} instances and their associated {@link PublishFilter} instances.
+ *
  * @author Hans Dockter
  */
 public interface PomFilterContainer {
@@ -77,18 +80,57 @@ public interface PomFilterContainer {
     MavenPom addFilter(String name, PublishFilter publishFilter);
 
     /**
-     * Returns a filter added with {@link #addFilter(String, org.gradle.api.artifacts.maven.PublishFilter)}
+     * Adds a publish filter.
+     *
+     * @param name   The name of the filter
+     * @param filter The filter
+     * @return The Maven pom associated with the closure
+     * @see PublishFilter
+     * @see PomFilterContainer#addFilter(String, org.gradle.api.artifacts.maven.PublishFilter)
+     */
+    MavenPom addFilter(String name, Closure filter);
+
+    /**
+     * Returns a filter added with {@link #addFilter(String, org.gradle.api.artifacts.maven.PublishFilter)}.
      *
      * @param name The name of the filter
      */
     PublishFilter filter(String name);
 
     /**
+     * Sets the default publish filter.
+     *
+     * @param filter The filter to be set
+     * @see PublishFilter
+     * @see PomFilterContainer#setFilter(org.gradle.api.artifacts.maven.PublishFilter)
+     */
+    void filter(Closure filter);
+
+    /**
      * Returns the pom associated with a filter added with {@link #addFilter(String, org.gradle.api.artifacts.maven.PublishFilter)}.
      *
      * @param name The name of the filter.
      */
     MavenPom pom(String name);
 
+    /**
+     * Configures a pom by a closure. The closure statements are delegated to the pom object associated with the given name.
+     *
+     * @param name
+     * @param configureClosure
+     * @return The pom object associated with the given name.
+     * @see PomFilterContainer#pom(String)
+     */
+    MavenPom pom(String name, Closure configureClosure);
+
+    /**
+     * Configures the default pom by a closure. The closure statements are delegated to the default pom.
+     *
+     * @param configureClosure
+     * @return The default pom.
+     * @see PomFilterContainer#getPom()
+     */
+    MavenPom pom(Closure configureClosure);
+
     Iterable<PomFilter> getActivePomFilters();
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PublishFilter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PublishFilter.java
index 41a469c..2461e95 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PublishFilter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/PublishFilter.java
@@ -21,6 +21,8 @@ import org.apache.ivy.core.module.descriptor.Artifact;
 import java.io.File;
 
 /**
+ * A filter for artifacts to be published.
+ *
  * @author Hans Dockter
  */
 public interface PublishFilter {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/XmlProvider.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/XmlProvider.java
index 77d68d2..2f87b43 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/XmlProvider.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/maven/XmlProvider.java
@@ -15,14 +15,36 @@
  */
 package org.gradle.api.artifacts.maven;
 
+import groovy.util.Node;
+import org.w3c.dom.Element;
+
 /**
+ * Provides various ways to access the content of an XML document.
+ *
  * @author Hans Dockter
  */
 public interface XmlProvider {
     /**
-     * Returns the xml in form of a StringBuilder. Changes to the StringBuilder instance will be applied against the XML.
+     * Returns the XML document as a {@link StringBuilder}. Changes to the returned instance will be applied to the XML.
+     * The returned instance is only valid until one of the other methods on this interface are called.
      *
-     * @return A StringBuilder representation of the XML
+     * @return A {@code StringBuilder} representation of the XML.
      */
     StringBuilder asString();
+
+    /**
+     * Returns the XML document as a Groovy {@link groovy.util.Node}. Changes to the returned instance will be applied
+     * to the XML. The returned instance is only valid until one of the other methods on this interface are called.
+     *
+     * @return A {@code Node} representation of the XML.
+     */
+    Node asNode();
+
+    /**
+     * Returns the XML document as a DOM {@link org.w3c.dom.Element}. Changes to the returned instance will be applied
+     * to the XML. The returned instance is only valid until one of the other methods on this interface are called.
+     *
+     * @return An {@code Element} representation of the XML.
+     */
+    Element asElement();
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/InternalRepository.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/InternalRepository.java
deleted file mode 100644
index 5e7383e..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/InternalRepository.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts.repositories;
-
-import org.apache.ivy.plugins.resolver.DependencyResolver;
-
-/**
- * @author Hans Dockter
- */
-public interface InternalRepository extends DependencyResolver {
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/WebdavResolver.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/WebdavResolver.java
index 90cf860..5a20d83 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/WebdavResolver.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/repositories/WebdavResolver.java
@@ -19,6 +19,8 @@ import org.apache.ivy.plugins.resolver.RepositoryResolver;
 import org.gradle.api.internal.artifacts.repositories.WebdavRepository;
 
 /**
+ * A {@link org.apache.ivy.plugins.resolver.RepositoryResolver} for webdav based repositories.
+ *
  * @author Hans Dockter
  */
 public class WebdavResolver extends RepositoryResolver {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java
index 9739e6a..eee91da 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencySpecs.java
@@ -15,11 +15,57 @@
  */
 package org.gradle.api.artifacts.specs;
 
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.specs.Spec;
+
 /**
+ * Various {@link Spec} implementations for selecting {@link Dependency} instances.
+ *
  * @author Hans Dockter
  */
 public class DependencySpecs {
-    public static DependencyTypeSpec type(Type type) {
-        return new DependencyTypeSpec(type);
+    public static Spec<Dependency> type(Type type) {
+        return new DependencyTypeSpec<Dependency>(type);
+    }
+
+    private static class DependencyTypeSpec<T extends Dependency> implements Spec<T> {
+
+        private Type type;
+
+        public DependencyTypeSpec(Type type) {
+            this.type = type;
+        }
+
+        public boolean isSatisfiedBy(Dependency dependency) {
+            return type.isOf(dependency);
+        }
+
+        public Type getType() {
+            return type;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            DependencyTypeSpec typeSpec = (DependencyTypeSpec) o;
+
+            if (type != typeSpec.type) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return type != null ? type.hashCode() : 0;
+        }
     }
+
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencyTypeSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencyTypeSpec.java
deleted file mode 100644
index fafc0c4..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/DependencyTypeSpec.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts.specs;
-
-import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.specs.Spec;
-
-/**
- * @author Hans Dockter
- */
-public class DependencyTypeSpec<T extends Dependency> implements Spec<T> {
-
-    private Type type;
-
-    public DependencyTypeSpec(Type type) {
-        this.type = type;
-    }
-
-    public boolean isSatisfiedBy(Dependency dependency) {
-        return type.isOf(dependency);
-    }
-
-    public Type getType() {
-        return type;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DependencyTypeSpec typeSpec = (DependencyTypeSpec) o;
-
-        if (type != typeSpec.type) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return type != null ? type.hashCode() : 0;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java
index cfcea71..02bc87f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/artifacts/specs/Type.java
@@ -20,6 +20,9 @@ import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.artifacts.ExternalDependency;
 
+/**
+ * Dependency types.
+ */
 public enum Type {
     EXTERNAL {public boolean isOf(Dependency dependency) {return dependency instanceof ExternalDependency;}},
     PROJECT {public boolean isOf(Dependency dependency) {return dependency instanceof ProjectDependency;}};
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/ContentFilterable.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/ContentFilterable.java
index 5e30ca3..722bd4b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/ContentFilterable.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/ContentFilterable.java
@@ -20,6 +20,9 @@ import groovy.lang.Closure;
 import java.io.FilterReader;
 import java.util.Map;
 
+/**
+ * Represents some binary resource whose content can be filtered.
+ */
 public interface ContentFilterable {
     /**
      * <p>Adds a content filter to be used during the copy.  Multiple calls to filter, add additional filters to the
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyAction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyAction.java
deleted file mode 100644
index 1605a2b..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyAction.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.file;
-
-import org.gradle.api.tasks.WorkResult;
-
-/**
- * @author Steve Appling
- */
-public interface CopyAction extends CopySpec, WorkResult {
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyProcessingSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyProcessingSpec.java
index 16e4901..8178ca4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyProcessingSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopyProcessingSpec.java
@@ -20,6 +20,9 @@ import org.gradle.api.Action;
 
 import java.util.regex.Pattern;
 
+/**
+ * Specifies the destination of a copy.
+ */
 public interface CopyProcessingSpec extends ContentFilterable {
     /**
      * Specifies the destination directory for a copy. The destination is evaluated as for {@link
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java
index a1ac6f1..2b6f00b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/CopySourceSpec.java
@@ -18,7 +18,7 @@ package org.gradle.api.file;
 import groovy.lang.Closure;
 
 /**
- * Specifies sources for a file copy
+ * Specifies sources for a file copy.
  *
  * @author Steve Appling
  */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/DeleteAction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/DeleteAction.java
index 9d536cd..4be5015 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/DeleteAction.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/DeleteAction.java
@@ -16,6 +16,8 @@
 package org.gradle.api.file;
 
 /**
+ * Deletes files and directories.
+ *
  * @author Hans Dockter
  */
 public interface DeleteAction {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/FileCollection.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/FileCollection.java
index d3bcd06..8d86ca0 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/FileCollection.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/FileCollection.java
@@ -151,6 +151,9 @@ public interface FileCollection extends Iterable<File>, AntBuilderAware, Buildab
      */
     FileTree getAsFileTree();
 
+    /**
+     * Ant types which a {@code FileCollection} can be mapped to.
+     */
     enum AntType {
         MatchingTask, FileSet, ResourceCollection
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/RelativePath.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/RelativePath.java
index f26671f..982b2a3 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/RelativePath.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/RelativePath.java
@@ -36,7 +36,7 @@ public class RelativePath {
     private final String[] segments;
 
     /**
-     * CTOR
+     * Creates a {@code RelativePath}.
      *
      * @param endsWithFile - if true, the path ends with a file, otherwise a directory
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/package-info.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/package-info.java
new file mode 100644
index 0000000..be592e2
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/file/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for working with files. 
+ */
+package org.gradle.api.file;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/initialization/dsl/package-info.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/initialization/dsl/package-info.java
new file mode 100644
index 0000000..9def7d1
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/initialization/dsl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes used in the initialization DSL. 
+ */
+package org.gradle.api.initialization.dsl;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java
index f461074..d0b8461 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractClassPathProvider.java
@@ -35,7 +35,7 @@ public abstract class AbstractClassPathProvider implements ClassPathProvider, Gr
     private final File gradleHome;
 
     protected AbstractClassPathProvider() {
-        File codeSource = findThisClass();
+        File codeSource = getClasspathForClass(DefaultClassPathProvider.class);
         if (codeSource.isFile()) {
             // Loaded from a JAR - assume we're running from the distribution
             gradleHome = codeSource.getParentFile().getParentFile();
@@ -84,10 +84,10 @@ public abstract class AbstractClassPathProvider implements ClassPathProvider, Gr
         return null;
     }
 
-    private File findThisClass() {
+    public static File getClasspathForClass(Class<?> targetClass) {
         URI location;
         try {
-            location = DefaultClassPathProvider.class.getProtectionDomain().getCodeSource().getLocation().toURI();
+            location = targetClass.getProtectionDomain().getCodeSource().getLocation().toURI();
         } catch (URISyntaxException e) {
             throw new UncheckedIOException(e);
         }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractTask.java
index 42f4f83..7729cf8 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/AbstractTask.java
@@ -100,11 +100,11 @@ public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {
         this.name = taskInfo.name;
         assert project != null;
         assert name != null;
-        path = project.absolutePath(name);
+        path = project.absoluteProjectPath(name);
         state = new TaskStateInternal(toString());
         dynamicObjectHelper = new DynamicObjectHelper(this, new DefaultConvention());
         dependencies = new DefaultTaskDependency(project.getTasks());
-        services = project.getServiceRegistryFactory().createFor(this);
+        services = project.getServices().createFor(this);
         outputs = services.get(TaskOutputsInternal.class);
         inputs = services.get(TaskInputs.class);
         executer = services.get(TaskExecuter.class);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java
index f5b8ce7..379a79b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/CachingDirectedGraphWalker.java
@@ -166,11 +166,9 @@ public class CachingDirectedGraphWalker<N, T> {
             this.graph = graph;
         }
 
-        @Override
         public void getEdgeValues(N from, N to, Collection<T> values) {
         }
 
-        @Override
         public void getNodeValues(N node, Collection<T> values, Collection<N> connectedNodes) {
             graph.getNodeValues(node, values, connectedNodes);
         }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/DomainObjectContext.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/DomainObjectContext.java
index 657572b..75365cc 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/DomainObjectContext.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/DomainObjectContext.java
@@ -16,5 +16,5 @@
 package org.gradle.api.internal;
 
 public interface DomainObjectContext {
-    String absolutePath(String name);
+    String absoluteProjectPath(String name);
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/Factory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/Factory.java
new file mode 100644
index 0000000..8a01b10
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/Factory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal;
+
+/**
+ * A generic factory which creates instances of type T.
+ *
+ * @param <T> The type of object created.
+ */
+public interface Factory<T> {
+    /**
+     * Creates a new instance of type T.
+     * @return The instance. Never returns null.
+     */
+    T create();
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GradleInternal.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GradleInternal.java
index 655eac1..fbc439d 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GradleInternal.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GradleInternal.java
@@ -73,5 +73,5 @@ public interface GradleInternal extends Gradle {
      */
     BuildListener getBuildListenerBroadcaster();
 
-    ServiceRegistryFactory getServiceRegistryFactory();
+    ServiceRegistryFactory getServices();
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java
index 71cb3ea..7657bc7 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/GraphAggregator.java
@@ -80,7 +80,6 @@ public class GraphAggregator<N> {
             this.graph = graph;
         }
 
-        @Override
         public void getNodeValues(N node, Collection<N> values, Collection<N> connectedNodes) {
             Set<N> edges = new LinkedHashSet<N>();
             graph.getNodeValues(node, new ArrayList(), edges);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java
new file mode 100644
index 0000000..1041de3
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/XmlTransformer.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal;
+
+import groovy.lang.Closure;
+import groovy.util.Node;
+import groovy.util.XmlNodePrinter;
+import groovy.util.XmlParser;
+import groovy.xml.XmlUtil;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.gradle.api.Action;
+import org.gradle.api.Transformer;
+import org.gradle.api.artifacts.maven.XmlProvider;
+import org.gradle.util.UncheckedException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class XmlTransformer implements Transformer<String> {
+    private final List<Action<? super XmlProvider>> actions = new ArrayList<Action<? super XmlProvider>>();
+
+    public void addAction(Action<? super XmlProvider> provider) {
+        actions.add(provider);
+    }
+
+    public void addAction(Closure closure) {
+        actions.add((Action<XmlProvider>) DefaultGroovyMethods.asType(closure, Action.class));
+    }
+
+    public String transform(String original) {
+        return doTransform(original).toString();
+    }
+
+    public void transform(String original, Writer destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    public void transform(Node original, Writer destination) {
+        doTransform(original).writeTo(destination);
+    }
+
+    private XmlProviderImpl doTransform(String original) {
+        XmlProviderImpl provider = new XmlProviderImpl(original);
+        provider.apply(actions);
+        return provider;
+    }
+
+    private XmlProviderImpl doTransform(Node original) {
+        XmlProviderImpl provider = new XmlProviderImpl(original);
+        provider.apply(actions);
+        return provider;
+    }
+
+    private static class XmlProviderImpl implements XmlProvider {
+        private StringBuilder builder;
+        private Node node;
+        private String stringValue;
+        private Element element;
+
+        public XmlProviderImpl(String original) {
+            this.stringValue = original;
+        }
+
+        public XmlProviderImpl(Node original) {
+            this.node = original;
+        }
+
+        public void apply(Iterable<Action<? super XmlProvider>> actions) {
+            for (Action<? super XmlProvider> action : actions) {
+                action.execute(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringWriter writer = new StringWriter();
+            writeTo(writer);
+            return writer.toString();
+        }
+
+        public void writeTo(Writer writer) {
+            try {
+                if (node != null) {
+                    PrintWriter printWriter = new PrintWriter(writer);
+                    new XmlNodePrinter(printWriter).print(node);
+                    printWriter.flush();
+                } else if (element != null) {
+                    PrintWriter printWriter = new PrintWriter(writer);
+                    XmlUtil.serialize(element, printWriter);
+                    printWriter.flush();
+                } else if (builder != null) {
+                    writer.append(builder);
+                } else {
+                    writer.append(stringValue);
+                }
+            } catch (IOException e) {
+                throw UncheckedException.asUncheckedException(e);
+            }
+        }
+
+        public StringBuilder asString() {
+            if (builder == null) {
+                builder = new StringBuilder(toString());
+                node = null;
+                element = null;
+            }
+            return builder;
+        }
+
+        public Node asNode() {
+            if (node == null) {
+                try {
+                    node = new XmlParser().parseText(toString());
+                } catch (Exception e) {
+                    throw UncheckedException.asUncheckedException(e);
+                }
+                builder = null;
+                element = null;
+            }
+            return node;
+        }
+
+        public Element asElement() {
+            if (element == null) {
+                Document document;
+                try {
+                    document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(toString())));
+                } catch (Exception e) {
+                    throw UncheckedException.asUncheckedException(e);
+                }
+                element = document.getDocumentElement();
+                builder = null;
+                node = null;
+            }
+            return element;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ProjectDependenciesBuildInstruction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ProjectDependenciesBuildInstruction.java
new file mode 100644
index 0000000..6e11d80
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ProjectDependenciesBuildInstruction.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts;
+
+import java.util.List;
+import java.util.Collections;
+
+/**
+ * @author Hans Dockter
+ */
+public class ProjectDependenciesBuildInstruction {
+    private List<String> taskNames;
+
+    public ProjectDependenciesBuildInstruction(List<String> taskNames) {
+        this.taskNames = taskNames;
+    }
+
+    public List<String> getTaskNames() {
+        if (taskNames == null) {
+            return Collections.emptyList();
+        }
+        return taskNames;
+    }
+
+    public boolean isRebuild() {
+        return taskNames != null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        ProjectDependenciesBuildInstruction that = (ProjectDependenciesBuildInstruction) o;
+
+        if (taskNames != null ? !taskNames.equals(that.taskNames) : that.taskNames != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return taskNames != null ? taskNames.hashCode() : 0;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
index dc72fa0..41831b6 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
@@ -44,7 +44,7 @@ public class DefaultConfigurationContainer extends AutoCreateDomainObjectContain
 
     @Override
     protected Configuration create(String name) {
-        return classGenerator.newInstance(DefaultConfiguration.class, context.absolutePath(name), name, this, ivyService);
+        return classGenerator.newInstance(DefaultConfiguration.class, context.absoluteProjectPath(name), name, this, ivyService);
     }
 
     @Override
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java
index 9a39f2d..45e55ca 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/configurations/DependencyMetaDataProvider.java
@@ -16,7 +16,7 @@
 package org.gradle.api.internal.artifacts.configurations;
 
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.repositories.InternalRepository;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 
 import java.io.File;
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
index 7d7eaa4..f937356 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
@@ -19,7 +19,7 @@ package org.gradle.api.internal.artifacts.dependencies;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
 import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.internal.artifacts.CachingDependencyResolveContext;
 import org.gradle.api.internal.artifacts.DependencyResolveContext;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/AbstractScriptTransformer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/AbstractScriptTransformer.java
index ec2f593..55595c4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/AbstractScriptTransformer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/AbstractScriptTransformer.java
@@ -50,6 +50,10 @@ public abstract class AbstractScriptTransformer extends CompilationUnit.SourceUn
     }
 
     protected ClassNode getScriptClass(SourceUnit source) {
+        if (source.getAST().getStatementBlock().getStatements().isEmpty() && source.getAST().getMethods().isEmpty()) {
+            // There is no script class when there are no statements or methods declared in the script
+            return null;
+        }
         return source.getAST().getClasses().get(0);
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ClasspathScriptTransformer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ClasspathScriptTransformer.java
index 06a9d23..34e86aa 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ClasspathScriptTransformer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/ClasspathScriptTransformer.java
@@ -89,6 +89,8 @@ public abstract class ClasspathScriptTransformer extends AbstractScriptTransform
         }
 
         ClassNode scriptClass = getScriptClass(source);
+
+        // Remove all the classes other than the main class
         Iterator<ClassNode> classes = source.getAST().getClasses().iterator();
         while (classes.hasNext()) {
             ClassNode classNode = classes.next();
@@ -97,12 +99,15 @@ public abstract class ClasspathScriptTransformer extends AbstractScriptTransform
             }
         }
 
-        for (MethodNode methodNode : new ArrayList<MethodNode>(scriptClass.getMethods())) {
-            if (!methodNode.getName().equals("run")) {
-                removeMethod(scriptClass, methodNode);
+        // Remove all the methods from the main class
+        if (scriptClass != null) {
+            for (MethodNode methodNode : new ArrayList<MethodNode>(scriptClass.getMethods())) {
+                if (!methodNode.getName().equals("run")) {
+                    removeMethod(scriptClass, methodNode);
+                }
             }
         }
-        
+
         source.getAST().getMethods().clear();
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactory.java
index b151639..a98ead0 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactory.java
@@ -15,16 +15,15 @@
  */
 package org.gradle.api.internal.artifacts.dsl;
 
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
-import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.internal.ClassGenerator;
-import org.gradle.api.internal.IConventionAware;
-import org.gradle.api.plugins.Convention;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
 
 /**
  * @author Hans Dockter
  */
-public class DefaultRepositoryHandlerFactory implements RepositoryHandlerFactory {
+public class DefaultRepositoryHandlerFactory implements Factory<RepositoryHandler> {
     private final ResolverFactory repositoryFactory;
     private final ClassGenerator classGenerator;
 
@@ -33,10 +32,7 @@ public class DefaultRepositoryHandlerFactory implements RepositoryHandlerFactory
         this.classGenerator = classGenerator;
     }
 
-    public DefaultRepositoryHandler createRepositoryHandler(Convention convention) {
-        DefaultRepositoryHandler repositoryHandler = classGenerator.newInstance(DefaultRepositoryHandler.class,
-                repositoryFactory, classGenerator);
-        ((IConventionAware) repositoryHandler).getConventionMapping().setConvention(convention);
-        return repositoryHandler;
+    public DefaultRepositoryHandler create() {
+        return classGenerator.newInstance(DefaultRepositoryHandler.class, repositoryFactory, classGenerator);
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/FixMainScriptTransformer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/FixMainScriptTransformer.java
index 519c759..806959f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/FixMainScriptTransformer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/FixMainScriptTransformer.java
@@ -38,6 +38,9 @@ public class FixMainScriptTransformer extends AbstractScriptTransformer {
     @Override
     public void call(SourceUnit source) throws CompilationFailedException {
         ClassNode scriptClass = getScriptClass(source);
+        if (scriptClass == null) {
+            return;
+        }
         for (MethodNode methodNode : scriptClass.getMethods()) {
             if (methodNode.getName().equals("main")) {
                 removeMethod(scriptClass, methodNode);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java
new file mode 100644
index 0000000..c347c2c
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/SharedConventionRepositoryHandlerFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.dsl;
+
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
+import org.gradle.api.internal.ConventionMapping;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.IConventionAware;
+import org.gradle.api.plugins.Convention;
+
+public class SharedConventionRepositoryHandlerFactory implements Factory<RepositoryHandler> {
+    private final Factory<? extends RepositoryHandler> factory;
+    private final Convention convention;
+    private ConventionMapping conventionMapping;
+
+    public SharedConventionRepositoryHandlerFactory(Factory<? extends RepositoryHandler> factory, Convention convention) {
+        this.factory = factory;
+        this.convention = convention;
+    }
+
+    public RepositoryHandler create() {
+        RepositoryHandler repositoryHandler = factory.create();
+        IConventionAware conventionAwareHandler = (IConventionAware) repositoryHandler;
+        if (conventionMapping == null) {
+            conventionMapping = conventionAwareHandler.getConventionMapping();
+            conventionMapping.setConvention(convention);
+        } else {
+            conventionAwareHandler.setConventionMapping(conventionMapping);
+        }
+        return repositoryHandler;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactory.java
index e76213f..5fbfae2 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactory.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.artifacts.dsl.dependencies;
 import org.gradle.api.IllegalDependencyNotation;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
 import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.internal.ClassGenerator;
 import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java
index 7e50744..f48ed9e 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyDependencyResolver.java
@@ -26,7 +26,6 @@ import org.gradle.api.internal.CachingDirectedGraphWalker;
 import org.gradle.api.internal.DirectedGraphWithEdgeValues;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.specs.Specs;
-import org.gradle.logging.IvyLoggingAdaper;
 import org.gradle.util.Clock;
 import org.gradle.util.WrapUtil;
 import org.slf4j.Logger;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java
index 2d2238e..f675538 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultResolverFactory.java
@@ -27,17 +27,19 @@ import org.gradle.api.artifacts.ResolverContainer;
 import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
 import org.gradle.api.artifacts.maven.GroovyMavenDeployer;
 import org.gradle.api.artifacts.maven.MavenResolver;
+import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.artifacts.publish.maven.DefaultArtifactPomFactory;
 import org.gradle.api.internal.artifacts.publish.maven.DefaultMavenPomFactory;
 import org.gradle.api.internal.artifacts.publish.maven.MavenPomMetaInfoProvider;
 import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultExcludeRuleConverter;
 import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultPomDependenciesConverter;
 import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenInstaller;
+import org.gradle.api.internal.artifacts.publish.maven.deploy.BasePomFilterContainer;
 import org.gradle.api.internal.artifacts.publish.maven.deploy.DefaultArtifactPomContainer;
 import org.gradle.api.internal.artifacts.publish.maven.deploy.groovy.DefaultGroovyMavenDeployer;
-import org.gradle.api.internal.artifacts.publish.maven.deploy.groovy.DefaultGroovyPomFilterContainer;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.logging.LoggingManagerFactory;
+import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.util.DeleteOnExit;
 
 import java.io.File;
@@ -48,9 +50,9 @@ import java.util.Map;
  * @author Hans Dockter
  */
 public class DefaultResolverFactory implements ResolverFactory {
-    private final LoggingManagerFactory loggingManagerFactory;
+    private final Factory<? extends LoggingManagerInternal> loggingManagerFactory;
 
-    public DefaultResolverFactory(LoggingManagerFactory loggingManagerFactory) {
+    public DefaultResolverFactory(Factory<? extends LoggingManagerInternal> loggingManagerFactory) {
         this.loggingManagerFactory = loggingManagerFactory;
     }
 
@@ -154,7 +156,7 @@ public class DefaultResolverFactory implements ResolverFactory {
     public GroovyMavenDeployer createMavenDeployer(String name, MavenPomMetaInfoProvider pomMetaInfoProvider,
                                                    ConfigurationContainer configurationContainer,
                                                    Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver) {
-        DefaultGroovyPomFilterContainer pomFilterContainer = new DefaultGroovyPomFilterContainer(
+        PomFilterContainer pomFilterContainer = new BasePomFilterContainer(
                 new DefaultMavenPomFactory(configurationContainer, scopeMapping, new DefaultPomDependenciesConverter(
                         new DefaultExcludeRuleConverter()), fileResolver));
         return new DefaultGroovyMavenDeployer(name, pomFilterContainer, new DefaultArtifactPomContainer(
@@ -166,7 +168,7 @@ public class DefaultResolverFactory implements ResolverFactory {
     public MavenResolver createMavenInstaller(String name, MavenPomMetaInfoProvider pomMetaInfoProvider,
                                               ConfigurationContainer configurationContainer,
                                               Conf2ScopeMappingContainer scopeMapping, FileResolver fileResolver) {
-        DefaultGroovyPomFilterContainer pomFilterContainer = new DefaultGroovyPomFilterContainer(
+        PomFilterContainer pomFilterContainer = new BasePomFilterContainer(
                 new DefaultMavenPomFactory(configurationContainer, scopeMapping, new DefaultPomDependenciesConverter(
                         new DefaultExcludeRuleConverter()), fileResolver));
         return new BaseMavenInstaller(name, pomFilterContainer, new DefaultArtifactPomContainer(pomMetaInfoProvider,
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
index 5112dd8..8ca8a69 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultSettingsConverter.java
@@ -133,8 +133,8 @@ public class DefaultSettingsConverter implements SettingsConverter {
         chainResolver.setChangingPattern(".*-SNAPSHOT");
         chainResolver.setChangingMatcher(PatternMatcher.REGEXP);
         chainResolver.setReturnFirst(true);
-        for (Object classpathResolver : classpathResolvers) {
-            chainResolver.add((DependencyResolver) classpathResolver);
+        for (DependencyResolver classpathResolver : classpathResolvers) {
+            chainResolver.add(classpathResolver);
         }
         return chainResolver;
     }
@@ -188,8 +188,8 @@ public class DefaultSettingsConverter implements SettingsConverter {
             }
             if (evt.getEventType() == TransferEvent.TRANSFER_STARTED) {
                 total = 0;
-                DefaultSettingsConverter.this.logger.lifecycle("Download " + evt.getResource().getName());
-                logger = progressLoggerFactory.start();
+                DefaultSettingsConverter.logger.lifecycle("Download " + evt.getResource().getName());
+                logger = progressLoggerFactory.start(DefaultSettingsConverter.class.getName());
             }
             if (evt.getEventType() == TransferEvent.TRANSFER_PROGRESS) {
                 total += evt.getLength();
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
new file mode 100644
index 0000000..3daaaff
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/IvyLoggingAdaper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.ivyservice;
+
+import org.apache.ivy.util.AbstractMessageLogger;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+
+/**
+ * This class is for integrating Ivy log statements into our logging system. We don't want to have a dependency on
+ * logback. This would be bad for embedded usage. We only want one on slf4j. But slf4j has no constants for log levels.
+ * As we want to avoid the execution of if statements for each Ivy request, we use Map which delegates Ivy log
+ * statements to Sl4j action classes.
+ *
+ * @author Hans Dockter
+ */
+public class IvyLoggingAdaper extends AbstractMessageLogger {
+    private final Logger logger = Logging.getLogger(IvyLoggingAdaper.class);
+
+    public void log(String msg, int level) {
+        logger.log(Logging.ANT_IVY_2_SLF4J_LEVEL_MAPPER.get(level), msg);
+    }
+
+    public void rawlog(String msg, int level) {
+        log(msg, level);
+    }
+
+    public void doProgress() {
+    }
+
+    public void doEndProgress(String msg) {
+        logger.info(msg);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java
index 9cc4020..c0fecc7 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/PublishModuleDescriptorConverter.java
@@ -49,7 +49,7 @@ public class PublishModuleDescriptorConverter implements ModuleDescriptorConvert
         Clock clock = new Clock();
         DefaultModuleDescriptor moduleDescriptor = (DefaultModuleDescriptor) resolveModuleDescriptorConverter.convert(configurations, module, settings);
         moduleDescriptor.addExtraAttributeNamespace(IVY_MAVEN_NAMESPACE_PREFIX, IVY_MAVEN_NAMESPACE);
-        artifactsToModuleDescriptorConverter.addArtifacts((DefaultModuleDescriptor) moduleDescriptor, configurations);
+        artifactsToModuleDescriptorConverter.addArtifacts(moduleDescriptor, configurations);
         logger.debug("Timing: Ivy convert for publish took {}", clock.getTime());
         return moduleDescriptor;
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
index 98bf094..3350c3b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPom.java
@@ -27,6 +27,7 @@ import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
 import org.gradle.api.artifacts.maven.MavenPom;
 import org.gradle.api.artifacts.maven.XmlProvider;
+import org.gradle.api.internal.XmlTransformer;
 import org.gradle.api.internal.artifacts.publish.maven.dependencies.PomDependenciesConverter;
 import org.gradle.api.internal.artifacts.publish.maven.pombuilder.CustomModelBuilder;
 import org.gradle.api.internal.file.FileResolver;
@@ -45,7 +46,7 @@ public class DefaultMavenPom implements MavenPom {
     private MavenProject mavenProject = new MavenProject();
     private Conf2ScopeMappingContainer scopeMappings;
     private ListenerBroadcast<Action> whenConfiguredActions = new ListenerBroadcast<Action>(Action.class);
-    private ListenerBroadcast<Action> withXmlActions = new ListenerBroadcast<Action>(Action.class);
+    private XmlTransformer withXmlActions = new XmlTransformer();
     private ConfigurationContainer configurations;
 
     public DefaultMavenPom(ConfigurationContainer configurationContainer, Conf2ScopeMappingContainer scopeMappings, PomDependenciesConverter pomDependenciesConverter,
@@ -192,7 +193,12 @@ public class DefaultMavenPom implements MavenPom {
             if (file.getParentFile() != null) {
                 file.getParentFile().mkdirs();
             }
-            return writeTo(new FileWriter(file));
+            FileWriter writer = new FileWriter(file);
+            try {
+                return writeTo(writer);
+            } finally {
+                writer.close();
+            }
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
@@ -202,13 +208,7 @@ public class DefaultMavenPom implements MavenPom {
         try {
             final StringWriter stringWriter = new StringWriter();
             mavenProject.writeModel(stringWriter);
-            final StringBuilder stringBuilder = new StringBuilder(stringWriter.toString());
-            withXmlActions.getSource().execute(new XmlProvider() {
-                public StringBuilder asString() {
-                    return stringBuilder;
-                }
-            });
-            IOUtils.write(stringBuilder.toString(), pomWriter);
+            withXmlActions.transform(stringWriter.toString(), pomWriter);
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         } finally {
@@ -227,14 +227,12 @@ public class DefaultMavenPom implements MavenPom {
     }
 
     public DefaultMavenPom withXml(final Closure closure) {
-        withXmlActions.add("execute", closure);
+        withXmlActions.addAction(closure);
         return this;
     }
 
     public DefaultMavenPom withXml(final Action<XmlProvider> action) {
-        withXmlActions.add(action);
+        withXmlActions.addAction(action);
         return this;
     }
-
-
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java
index d074740..fdf2b7d 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactory.java
@@ -18,6 +18,7 @@ package org.gradle.api.internal.artifacts.publish.maven;
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer;
 import org.gradle.api.artifacts.maven.MavenPom;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.artifacts.publish.maven.dependencies.DefaultConf2ScopeMappingContainer;
 import org.gradle.api.internal.artifacts.publish.maven.dependencies.PomDependenciesConverter;
 import org.gradle.api.internal.file.FileResolver;
@@ -25,7 +26,7 @@ import org.gradle.api.internal.file.FileResolver;
 /**
  * @author Hans Dockter
  */
-public class DefaultMavenPomFactory implements MavenPomFactory {
+public class DefaultMavenPomFactory implements Factory<MavenPom> {
     private ConfigurationContainer configurationContainer;
     private Conf2ScopeMappingContainer conf2ScopeMappingContainer;
     private PomDependenciesConverter pomDependenciesConverter;
@@ -40,7 +41,7 @@ public class DefaultMavenPomFactory implements MavenPomFactory {
         this.fileResolver = fileResolver;
     }
 
-    public MavenPom createMavenPom() {
+    public MavenPom create() {
         return new DefaultMavenPom(configurationContainer,
                 new DefaultConf2ScopeMappingContainer(conf2ScopeMappingContainer.getMappings()), pomDependenciesConverter, fileResolver);
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/MavenPomFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/MavenPomFactory.java
deleted file mode 100644
index f53b20a..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/MavenPomFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.publish.maven;
-
-import org.gradle.api.artifacts.maven.MavenPom;
-
-/**
- * @author Hans Dockter
- */
-public interface MavenPomFactory {
-    MavenPom createMavenPom();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
index faa37f4..2aea790 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/AbstractMavenResolver.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.artifacts.publish.maven.deploy;
 
+import groovy.lang.Closure;
 import org.apache.commons.io.FileUtils;
 import org.apache.ivy.core.cache.ArtifactOrigin;
 import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
@@ -252,6 +253,14 @@ public abstract class AbstractMavenResolver implements MavenResolver {
         return pomFilterContainer.addFilter(name, publishFilter);
     }
 
+    public MavenPom addFilter(String name, Closure filter) {
+        return pomFilterContainer.addFilter(name, filter);
+    }
+
+    public void filter(Closure filter) {
+        pomFilterContainer.filter(filter);
+    }
+
     public PublishFilter filter(String name) {
         return pomFilterContainer.filter(name);
     }
@@ -260,6 +269,14 @@ public abstract class AbstractMavenResolver implements MavenResolver {
         return pomFilterContainer.pom(name);
     }
 
+    public MavenPom pom(Closure configureClosure) {
+        return pomFilterContainer.pom(configureClosure);
+    }
+
+    public MavenPom pom(String name, Closure configureClosure) {
+        return pomFilterContainer.pom(name, configureClosure);
+    }
+
     public Iterable<PomFilter> getActivePomFilters() {
         return pomFilterContainer.getActivePomFilters();
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
index 3f706b7..2169ed6 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployer.java
@@ -24,6 +24,7 @@ import org.codehaus.plexus.PlexusContainerException;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.maven.MavenDeployer;
 import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.Factory;
 import org.gradle.logging.LoggingManagerInternal;
 
 import java.io.File;
@@ -39,7 +40,7 @@ public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDep
 
     private RemoteRepository remoteSnapshotRepository;
 
-    private DeployTaskFactory deployTaskFactory = new DefaultDeployTaskFactory();
+    private Factory<CustomDeployTask> deployTaskFactory = new DefaultDeployTaskFactory();
 
     private Configuration configuration;
 
@@ -53,7 +54,7 @@ public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDep
     }
 
     protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
-        CustomDeployTask deployTask = deployTaskFactory.createDeployTask();
+        CustomDeployTask deployTask = deployTaskFactory.create();
         deployTask.setProject(project);
         deployTask.setUniqueVersion(isUniqueVersion());
         addProtocolProvider(deployTask);
@@ -97,11 +98,11 @@ public class BaseMavenDeployer extends AbstractMavenResolver implements MavenDep
         this.remoteSnapshotRepository = remoteSnapshotRepository;
     }
 
-    public DeployTaskFactory getDeployTaskFactory() {
+    public Factory<CustomDeployTask> getDeployTaskFactory() {
         return deployTaskFactory;
     }
 
-    public void setDeployTaskFactory(DeployTaskFactory deployTaskFactory) {
+    public void setDeployTaskFactory(Factory<CustomDeployTask> deployTaskFactory) {
         this.deployTaskFactory = deployTaskFactory;
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
index 53f2df0..69e18bc 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstaller.java
@@ -19,29 +19,30 @@ import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
 import org.apache.maven.artifact.ant.InstallTask;
 import org.apache.tools.ant.Project;
 import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.Factory;
 import org.gradle.logging.LoggingManagerInternal;
 
 /**
  * @author Hans Dockter
  */
 public class BaseMavenInstaller extends AbstractMavenResolver {
-    private InstallTaskFactory installTaskFactory = new DefaultInstallTaskFactory();
+    private Factory<CustomInstallTask> installTaskFactory = new DefaultInstallTaskFactory();
 
     public BaseMavenInstaller(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
         super(name, pomFilterContainer, artifactPomContainer, loggingManager);
     }
 
     protected InstallDeployTaskSupport createPreConfiguredTask(Project project) {
-        InstallTask installTask = installTaskFactory.createInstallTask();
+        InstallTask installTask = installTaskFactory.create();
         installTask.setProject(project);
         return installTask;
     }
 
-    public InstallTaskFactory getInstallTaskFactory() {
+    public Factory<CustomInstallTask> getInstallTaskFactory() {
         return installTaskFactory;
     }
 
-    public void setInstallTaskFactory(InstallTaskFactory installTaskFactory) {
+    public void setInstallTaskFactory(Factory<CustomInstallTask> installTaskFactory) {
         this.installTaskFactory = installTaskFactory;
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
index bb770c2..f11bc92 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainer.java
@@ -15,11 +15,14 @@
  */
 package org.gradle.api.internal.artifacts.publish.maven.deploy;
 
+import groovy.lang.Closure;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.maven.MavenPom;
 import org.gradle.api.artifacts.maven.PomFilterContainer;
 import org.gradle.api.artifacts.maven.PublishFilter;
-import org.gradle.api.internal.artifacts.publish.maven.MavenPomFactory;
+import org.gradle.api.internal.Factory;
+import org.gradle.util.ConfigureUtil;
 import org.gradle.util.WrapUtil;
 
 import java.util.HashMap;
@@ -33,9 +36,9 @@ public class BasePomFilterContainer implements PomFilterContainer {
 
     private PomFilter defaultPomFilter;
 
-    private MavenPomFactory mavenPomFactory;
+    private Factory<? extends MavenPom> mavenPomFactory;
 
-    public BasePomFilterContainer(MavenPomFactory mavenPomFactory) {
+    public BasePomFilterContainer(Factory<? extends MavenPom> mavenPomFactory) {
         this.mavenPomFactory = mavenPomFactory;
     }
 
@@ -55,11 +58,31 @@ public class BasePomFilterContainer implements PomFilterContainer {
         getDefaultPomFilter().setPomTemplate(defaultPom);
     }
 
+    public void filter(Closure filter) {
+        setFilter(toFilter(filter));
+    }
+
+    public MavenPom addFilter(String name, Closure filter) {
+        return addFilter(name, toFilter(filter));
+    }
+
+    private PublishFilter toFilter(final Closure filter) {
+        return (PublishFilter) DefaultGroovyMethods.asType(filter, PublishFilter.class);
+    }
+
+    public MavenPom pom(Closure configureClosure) {
+        return ConfigureUtil.configure(configureClosure, getPom());
+    }
+
+    public MavenPom pom(String name, Closure configureClosure) {
+        return ConfigureUtil.configure(configureClosure, pom(name));
+    }
+
     public MavenPom addFilter(String name, PublishFilter publishFilter) {
         if (name == null || publishFilter == null) {
             throw new InvalidUserDataException("Name and Filter must not be null.");
         }
-        MavenPom pom = mavenPomFactory.createMavenPom();
+        MavenPom pom = mavenPomFactory.create();
         pomFilters.put(name, new DefaultPomFilter(name, pom, publishFilter));
         return pom;
     }
@@ -88,13 +111,13 @@ public class BasePomFilterContainer implements PomFilterContainer {
         return activeArtifactPoms;
     }
 
-    public MavenPomFactory getMavenPomFactory() {
+    public Factory<? extends MavenPom> getMavenPomFactory() {
         return mavenPomFactory;
     }
 
     public PomFilter getDefaultPomFilter() {
         if (defaultPomFilter == null) {
-            defaultPomFilter = new DefaultPomFilter(PomFilterContainer.DEFAULT_ARTIFACT_POM_NAME, mavenPomFactory.createMavenPom(),
+            defaultPomFilter = new DefaultPomFilter(PomFilterContainer.DEFAULT_ARTIFACT_POM_NAME, mavenPomFactory.create(),
                 PublishFilter.ALWAYS_ACCEPT);
         }
         return defaultPomFilter;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java
index 3266ce0..7927936 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultDeployTaskFactory.java
@@ -15,11 +15,13 @@
  */
 package org.gradle.api.internal.artifacts.publish.maven.deploy;
 
+import org.gradle.api.internal.Factory;
+
 /**
  * @author Hans Dockter
  */
-public class DefaultDeployTaskFactory implements DeployTaskFactory {
-    public CustomDeployTask createDeployTask() {
+public class DefaultDeployTaskFactory implements Factory<CustomDeployTask> {
+    public CustomDeployTask create() {
         return new CustomDeployTask();
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java
index ec16fdc..a7e0e29 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DefaultInstallTaskFactory.java
@@ -15,11 +15,13 @@
  */
 package org.gradle.api.internal.artifacts.publish.maven.deploy;
 
+import org.gradle.api.internal.Factory;
+
 /**
  * @author Hans Dockter
  */
-public class DefaultInstallTaskFactory implements InstallTaskFactory {
-    public CustomInstallTask createInstallTask() {
+public class DefaultInstallTaskFactory implements Factory<CustomInstallTask> {
+    public CustomInstallTask create() {
         return new CustomInstallTask();
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DeployTaskFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DeployTaskFactory.java
deleted file mode 100644
index e598e45..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/DeployTaskFactory.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.publish.maven.deploy;
-
-/**
- * @author Hans Dockter
- */
-public interface DeployTaskFactory {
-    CustomDeployTask createDeployTask();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/InstallTaskFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/InstallTaskFactory.java
deleted file mode 100644
index 1e366e9..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/InstallTaskFactory.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.publish.maven.deploy;
-
-/**
- * @author Hans Dockter
- */
-public interface InstallTaskFactory {
-    CustomInstallTask createInstallTask();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
index 4b14a73..663db2e 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployer.groovy
@@ -17,8 +17,7 @@ package org.gradle.api.internal.artifacts.publish.maven.deploy.groovy
 
 import org.codehaus.groovy.runtime.InvokerHelper
 import org.gradle.api.artifacts.maven.GroovyMavenDeployer
-import org.gradle.api.artifacts.maven.GroovyPomFilterContainer
-import org.gradle.api.artifacts.maven.MavenPom
+import org.gradle.api.artifacts.maven.PomFilterContainer
 import org.gradle.api.internal.artifacts.publish.maven.deploy.ArtifactPomContainer
 import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployer
 import org.gradle.logging.LoggingManagerInternal
@@ -26,13 +25,13 @@ import org.gradle.logging.LoggingManagerInternal
 /**
  * @author Hans Dockter
  */
-class DefaultGroovyMavenDeployer extends BaseMavenDeployer implements GroovyMavenDeployer, GroovyPomFilterContainer {
+class DefaultGroovyMavenDeployer extends BaseMavenDeployer implements GroovyMavenDeployer, PomFilterContainer {
     public static final String REPOSITORY_BUILDER = "repository"
     public static final String SNAPSHOT_REPOSITORY_BUILDER = 'snapshotRepository'
     
     private RepositoryBuilder repositoryBuilder = new RepositoryBuilder()
 
-    DefaultGroovyMavenDeployer(String name, GroovyPomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
+    DefaultGroovyMavenDeployer(String name, PomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManagerInternal loggingManager) {
         super(name, pomFilterContainer, artifactPomContainer, loggingManager)
     }
     
@@ -49,21 +48,4 @@ class DefaultGroovyMavenDeployer extends BaseMavenDeployer implements GroovyMave
             throw new MissingMethodException(name, this.class, args)
         }
     }
-
-    void filter(Closure filter) {
-        getPomFilterContainer().filter(filter)
-    }
-
-    MavenPom addFilter(String name, Closure filter) {
-        getPomFilterContainer().addFilter(name, filter)
-    }
-
-    MavenPom pom(Closure configureClosure) {
-        getPomFilterContainer().pom(configureClosure)
-    }
-
-    MavenPom pom(String name, Closure configureClosure) {
-        getPomFilterContainer().pom(name, configureClosure)
-    }
-
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenInstaller.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenInstaller.groovy
deleted file mode 100644
index 625ef82..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenInstaller.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.publish.maven.deploy.groovy
-
-import org.gradle.api.artifacts.maven.GroovyPomFilterContainer
-import org.gradle.api.artifacts.maven.MavenPom
-import org.gradle.api.internal.artifacts.publish.maven.deploy.ArtifactPomContainer
-import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenInstaller
-import org.gradle.api.logging.LoggingManager
-
-/**
- * @author Hans Dockter
- */
-public class DefaultGroovyMavenInstaller extends BaseMavenInstaller implements GroovyPomFilterContainer {
-    DefaultGroovyMavenInstaller(String name, GroovyPomFilterContainer pomFilterContainer, ArtifactPomContainer artifactPomContainer, LoggingManager loggingManager) {
-        super(name, pomFilterContainer, artifactPomContainer, loggingManager)
-    }
-
-    void filter(Closure filter) {
-        getPomFilterContainer().filter(filter)
-    }
-
-    MavenPom addFilter(String name, Closure filter) {
-        getPomFilterContainer().addFilter(name, filter)
-    }
-
-    MavenPom pom(Closure configureClosure) {
-        getPomFilterContainer().pom(configureClosure)
-    }
-
-    MavenPom pom(String name, Closure configureClosure) {
-        getPomFilterContainer().pom(name, configureClosure)
-    }
-
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyPomFilterContainer.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyPomFilterContainer.groovy
deleted file mode 100644
index 506e679..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyPomFilterContainer.groovy
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.artifacts.publish.maven.deploy.groovy
-
-import org.gradle.api.artifacts.maven.MavenPom
-import org.gradle.api.artifacts.maven.PublishFilter
-import org.gradle.api.internal.artifacts.publish.maven.MavenPomFactory
-import org.gradle.api.internal.artifacts.publish.maven.deploy.BasePomFilterContainer
-import org.gradle.util.ConfigureUtil
-import org.gradle.api.artifacts.maven.GroovyPomFilterContainer;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultGroovyPomFilterContainer extends BasePomFilterContainer implements GroovyPomFilterContainer {
-    DefaultGroovyPomFilterContainer(MavenPomFactory mavenPomFactory) {
-        super(mavenPomFactory);
-    }
-
-    void filter(Closure filter) {
-        this.filter = filter as PublishFilter
-    }
-
-    MavenPom addFilter(String name, Closure filter) {
-        addFilter(name, filter as PublishFilter)
-    }
-
-    MavenPom pom(Closure configureClosure) {
-        ConfigureUtil.configure(configureClosure, pom)
-    }
-
-    MavenPom pom(String name, Closure configureClosure) {
-        ConfigureUtil.configure(configureClosure, pom(name))
-    }
-
-    protected BasePomFilterContainer newInstance() {
-        return new DefaultGroovyPomFilterContainer(mavenPomFactory);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java
index d7df344..8c9ad18 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/publish/maven/pombuilder/CustomModelBuilder.java
@@ -30,10 +30,8 @@ import java.util.Map;
  * This is a slightly modified version as shipped with polyglot Maven.
  */
 public class CustomModelBuilder extends ModelBuilder {
-    private Model model;
 
     public CustomModelBuilder(Model model) {
-        this.model = model;
         ExecuteManager executeManager = new ExecuteManagerImpl();
         setProp(executeManager.getClass(), executeManager, "log",
                 new PlexusLoggerAdapter(LoggerFactory.getLogger(ExecuteManagerImpl.class)));
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java
index 7c908b8..5dc24ad 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/DefaultInternalRepository.java
@@ -17,7 +17,10 @@ package org.gradle.api.internal.artifacts.repositories;
 
 import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.cache.ArtifactOrigin;
-import org.apache.ivy.core.module.descriptor.*;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.report.ArtifactDownloadReport;
 import org.apache.ivy.core.report.DownloadReport;
 import org.apache.ivy.core.report.DownloadStatus;
@@ -31,9 +34,8 @@ import org.apache.ivy.plugins.repository.file.FileResource;
 import org.apache.ivy.plugins.resolver.BasicResolver;
 import org.apache.ivy.plugins.resolver.util.ResolvedResource;
 import org.gradle.api.Project;
+import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.ResolverContainer;
-import org.gradle.api.artifacts.repositories.InternalRepository;
-import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyDependencyPublisher;
 import org.gradle.api.internal.artifacts.ivyservice.ModuleDescriptorConverter;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
@@ -87,9 +89,9 @@ public class DefaultInternalRepository extends BasicResolver implements Internal
             return null;
         }
         Project project = gradle.getRootProject().project(projectPathValue);
-        DependencyMetaDataProvider dependencyMetaDataProvider = ((ProjectInternal) project).getServiceRegistryFactory().get(DependencyMetaDataProvider.class);
+        Module projectModule = ((ProjectInternal) project).getModule();
         ModuleDescriptor projectDescriptor = moduleDescriptorConverter.convert(project.getConfigurations().getAll(),
-                dependencyMetaDataProvider.getModule(), IvyContext.getContext().getIvy().getSettings());
+                projectModule, IvyContext.getContext().getIvy().getSettings());
 
         for (DependencyArtifactDescriptor artifactDescriptor : descriptor.getAllDependencyArtifacts()) {
             for (Artifact artifact : projectDescriptor.getAllArtifacts()) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/InternalRepository.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/InternalRepository.java
new file mode 100644
index 0000000..204ba40
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/artifacts/repositories/InternalRepository.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts.repositories;
+
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * @author Hans Dockter
+ */
+public interface InternalRepository extends DependencyResolver {
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
index f3ca339..0f9e745 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/changedetection/DefaultTaskArtifactStateRepository.java
@@ -299,7 +299,7 @@ public class DefaultTaskArtifactStateRepository implements TaskArtifactStateRepo
                     formatter.format("%n%s", message);
                 }
                 if (messages.size() > MAX_OUT_OF_DATE_MESSAGES) {
-                    formatter.format("%d more ...", messages.size() - MAX_OUT_OF_DATE_MESSAGES);
+                    formatter.format("%n%d more ...", messages.size() - MAX_OUT_OF_DATE_MESSAGES);
                 }
                 LOGGER.info(formatter.toString());
             }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/RelativePathSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/RelativePathSpec.java
new file mode 100644
index 0000000..2bf11e3
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/RelativePathSpec.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file;
+
+import org.gradle.api.file.FileTreeElement;
+import org.gradle.api.file.RelativePath;
+import org.gradle.api.specs.Spec;
+
+public class RelativePathSpec implements Spec<FileTreeElement> {
+    private final Spec<? super RelativePath> pathSpec;
+
+    public RelativePathSpec(Spec<? super RelativePath> pathSpec) {
+        this.pathSpec = pathSpec;
+    }
+
+    public boolean isSatisfiedBy(FileTreeElement element) {
+        return pathSpec.isSatisfiedBy(element.getRelativePath());
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java
index 3649207..75bfe6f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/TarCopySpecVisitor.java
@@ -17,7 +17,7 @@ package org.gradle.api.internal.file.archive;
 
 import org.gradle.api.GradleException;
 import org.gradle.api.UncheckedIOException;
-import org.gradle.api.file.CopyAction;
+import org.gradle.api.internal.file.copy.CopyAction;
 import org.gradle.api.file.FileVisitDetails;
 import org.apache.tools.tar.TarOutputStream;
 import org.apache.tools.tar.TarEntry;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java
index f3a2814..bb6cc3c 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/archive/ZipCopySpecVisitor.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.file.archive;
 import org.apache.tools.zip.*;
 import org.gradle.api.GradleException;
 import org.gradle.api.UncheckedIOException;
-import org.gradle.api.file.CopyAction;
+import org.gradle.api.internal.file.copy.CopyAction;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.internal.file.copy.ArchiveCopyAction;
 import org.gradle.api.internal.file.copy.EmptyCopySpecVisitor;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java
index 3c2c1b6..8058188 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/ArchiveCopyAction.java
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.internal.file.copy;
 
-import org.gradle.api.file.CopyAction;
-
 import java.io.File;
 
 public interface ArchiveCopyAction extends CopyAction {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java
new file mode 100644
index 0000000..5c3a686
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopyAction.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.file.copy;
+
+import org.gradle.api.file.CopySpec;
+import org.gradle.api.tasks.WorkResult;
+
+/**
+ * @author Steve Appling
+ */
+public interface CopyAction extends CopySpec, WorkResult {
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
index 202c7d9..c0f846d 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecImpl.java
@@ -78,7 +78,11 @@ public class CopySpecImpl implements CopySpec, ReadableCopySpec {
 
     public CopySpec from(Object... sourcePaths) {
         for (Object sourcePath : sourcePaths) {
-            this.sourcePaths.add(sourcePath);
+            if (sourcePath instanceof CopySpec) {
+                with((CopySpec) sourcePath);
+            } else {
+                this.sourcePaths.add(sourcePath);
+            }
         }
         return this;
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java
index a93391e..46f2cf9 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/CopySpecVisitor.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.file.copy;
 
 import org.gradle.api.file.FileVisitor;
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.tasks.WorkResult;
 
 public interface CopySpecVisitor extends FileVisitor, WorkResult {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java
index c63d56c..0a5ee3a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/DelegatingCopySpecVisitor.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.file.copy;
 
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.file.FileVisitDetails;
 
 public class DelegatingCopySpecVisitor implements CopySpecVisitor {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java
index e1e23a2..0c9dbce 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/EmptyCopySpecVisitor.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.file.copy;
 
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.file.FileVisitDetails;
 
 public class EmptyCopySpecVisitor implements CopySpecVisitor {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java
index b9ba983..7040c9d 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopyAction.java
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.internal.file.copy;
 
-import org.gradle.api.file.CopyAction;
-
 import java.io.File;
 
 public interface FileCopyAction extends CopyAction {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
index 881b017..82036aa 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/FileCopySpecVisitor.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.file.copy;
 
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.file.FileTreeElement;
 import org.gradle.api.file.FileVisitDetails;
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
index 42ca23d..7eda366 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitor.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.file.copy;
 
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.FileVisitor;
 import org.gradle.api.file.RelativePath;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java
index 5e34418..bdf5372 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactory.java
@@ -21,14 +21,13 @@ import org.gradle.api.UnknownProjectException;
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
 import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
 import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
-import org.gradle.api.internal.plugins.DefaultConvention;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.util.ObservableUrlClassLoader;
 
@@ -38,7 +37,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
-    private final RepositoryHandlerFactory repositoryHandlerFactory;
+    private final Factory<? extends RepositoryHandler> repositoryHandlerFactory;
     private final ConfigurationContainerFactory configurationContainerFactory;
     private final DependencyMetaDataProvider dependencyMetaDataProvider;
     private final DependencyFactory dependencyFactory;
@@ -49,7 +48,7 @@ public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
         }
     };
 
-    public DefaultScriptHandlerFactory(RepositoryHandlerFactory repositoryHandlerFactory,
+    public DefaultScriptHandlerFactory(Factory<? extends RepositoryHandler> repositoryHandlerFactory,
                                        ConfigurationContainerFactory configurationContainerFactory,
                                        DependencyMetaDataProvider dependencyMetaDataProvider,
                                        DependencyFactory dependencyFactory) {
@@ -65,7 +64,7 @@ public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
 
     public ScriptHandlerInternal create(ScriptSource scriptSource, ClassLoader parentClassLoader,
                                         DomainObjectContext context) {
-        RepositoryHandler repositoryHandler = repositoryHandlerFactory.createRepositoryHandler(new DefaultConvention());
+        RepositoryHandler repositoryHandler = repositoryHandlerFactory.create();
         ConfigurationContainer configurationContainer = configurationContainerFactory.createConfigurationContainer(
                 repositoryHandler, dependencyMetaDataProvider, context);
         DependencyHandler dependencyHandler = new DefaultDependencyHandler(configurationContainer, dependencyFactory,
@@ -83,7 +82,7 @@ public class DefaultScriptHandlerFactory implements ScriptHandlerFactory {
     }
 
     private static class BasicDomainObjectContext implements DomainObjectContext {
-        public String absolutePath(String name) {
+        public String absoluteProjectPath(String name) {
             return name;
         }
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java
index 4f5f853..4a1b311 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AbstractProject.java
@@ -25,7 +25,6 @@ import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.dsl.ArtifactHandler;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.ConfigurableFileTree;
 import org.gradle.api.file.CopySpec;
@@ -38,7 +37,10 @@ import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
 import org.gradle.api.internal.plugins.DefaultObjectConfigurationAction;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
-import org.gradle.api.logging.*;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.PluginContainer;
 import org.gradle.api.tasks.Directory;
@@ -54,7 +56,7 @@ import org.gradle.process.ExecResult;
 import org.gradle.util.Configurable;
 import org.gradle.util.ConfigureUtil;
 import org.gradle.util.DeprecationLogger;
-import org.gradle.util.PathHelper;
+import org.gradle.util.Path;
 
 import java.io.File;
 import java.net.URI;
@@ -70,7 +72,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     private static Logger buildLogger = Logging.getLogger(Project.class);
     private ServiceRegistryFactory services;
 
-    private final Project rootProject;
+    private final ProjectInternal rootProject;
 
     private final GradleInternal gradle;
 
@@ -101,7 +103,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     private FileResolver fileResolver;
     private FileOperations fileOperations;
 
-    private AntBuilderFactory antBuilderFactory;
+    private Factory<? extends AntBuilder> antBuilderFactory;
 
     private AntBuilder ant;
 
@@ -109,12 +111,12 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     private PluginContainer pluginContainer;
 
-    private final String path;
-
     private final int depth;
 
     private TaskContainerInternal taskContainer;
 
+    private TaskContainerInternal implicitTasksContainer;
+
     private IProjectRegistry<ProjectInternal> projectRegistry;
 
     private DependencyHandler dependencyHandler;
@@ -123,7 +125,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     private ArtifactHandler artifactHandler;
 
-    private RepositoryHandlerFactory repositoryHandlerFactory;
+    private Factory<? extends RepositoryHandler> repositoryHandlerFactory;
 
     private RepositoryHandler repositoryHandler;
 
@@ -137,6 +139,10 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     private DynamicObjectHelper dynamicObjectHelper;
 
+    private String description;
+
+    private final Path path;
+
     public AbstractProject(String name,
                            ProjectInternal parent,
                            File projectDir,
@@ -153,21 +159,23 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         this.gradle = gradle;
 
         if (parent == null) {
-            path = Project.PATH_SEPARATOR;
+            path = Path.ROOT;
             depth = 0;
         } else {
-            path = parent.absolutePath(name);
+            String path = parent.absoluteProjectPath(name);
             depth = parent.getDepth() + 1;
+            this.path = Path.path(path);
         }
 
         services = serviceRegistryFactory.createFor(this);
         fileResolver = services.get(FileResolver.class);
+        antBuilderFactory = services.getFactory(AntBuilder.class);
+        taskContainer = services.newInstance(TaskContainerInternal.class);
+        implicitTasksContainer = services.newInstance(TaskContainerInternal.class);
         fileOperations = services.get(FileOperations.class);
-        antBuilderFactory = services.get(AntBuilderFactory.class);
-        taskContainer = services.get(TaskContainerInternal.class);
-        repositoryHandlerFactory = services.get(RepositoryHandlerFactory.class);
+        repositoryHandlerFactory = services.getFactory(RepositoryHandler.class);
         projectEvaluator = services.get(ProjectEvaluator.class);
-        repositoryHandler = services.get(RepositoryHandler.class);
+        repositoryHandler = repositoryHandlerFactory.create();
         configurationContainer = services.get(ConfigurationContainer.class);
         pluginContainer = services.get(PluginContainer.class);
         artifactHandler = services.get(ArtifactHandler.class);
@@ -188,12 +196,10 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     }
 
     public RepositoryHandler createRepositoryHandler() {
-        RepositoryHandler handler = repositoryHandlerFactory.createRepositoryHandler(getConvention());
-        ((IConventionAware) handler).setConventionMapping(((IConventionAware) repositoryHandler).getConventionMapping());
-        return handler;
+        return repositoryHandlerFactory.create();
     }
 
-    public Project getRootProject() {
+    public ProjectInternal getRootProject() {
         return rootProject;
     }
 
@@ -269,6 +275,14 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return name;
     }
 
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
     public Object getGroup() {
         if (group != null) {
             return group;
@@ -346,7 +360,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return repositoryHandler;
     }
 
-    public RepositoryHandlerFactory getRepositoryHandlerFactory() {
+    public Factory<? extends RepositoryHandler> getRepositoryHandlerFactory() {
         return repositoryHandlerFactory;
     }
 
@@ -376,7 +390,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     }
 
     public String getPath() {
-        return path;
+        return path.toString();
     }
 
     public int getDepth() {
@@ -413,22 +427,23 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     public int compareTo(Project otherProject) {
         int depthCompare = depthCompare(otherProject);
         if (depthCompare == 0) {
-            return path.compareTo(otherProject.getPath());
+            return getPath().compareTo(otherProject.getPath());
         } else {
             return depthCompare;
         }
     }
 
     public String absolutePath(String path) {
-        if (!isAbsolutePath(path)) {
-            String prefix = this == rootProject ? "" : Project.PATH_SEPARATOR;
-            return this.path + prefix + path;
-        }
-        return path;
+        DeprecationLogger.nagUser("Project.absolutePath()", "Project.absoluteProjectPath()");
+        return absoluteProjectPath(path);
+    }
+
+    public String absoluteProjectPath(String path) {
+        return this.path.absolutePath(path);
     }
 
-    public static boolean isAbsolutePath(String path) {
-        return PathHelper.isAbsolutePath(path);
+    public String relativeProjectPath(String path) {
+        return this.path.relativePath(path);
     }
 
     public Project project(String path) {
@@ -443,15 +458,15 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         if (!isTrue(path)) {
             throw new InvalidUserDataException("A path must be specified!");
         }
-        return projectRegistry.getProject(isAbsolutePath(path) ? path : absolutePath(path));
+        return projectRegistry.getProject(absoluteProjectPath(path));
     }
 
     public Set<Project> getAllprojects() {
-        return new TreeSet<Project>(projectRegistry.getAllProjects(this.path));
+        return new TreeSet<Project>(projectRegistry.getAllProjects(getPath()));
     }
 
     public Set<Project> getSubprojects() {
-        return new TreeSet<Project>(projectRegistry.getSubProjects(this.path));
+        return new TreeSet<Project>(projectRegistry.getSubProjects(getPath()));
     }
 
     public void subprojects(Action<? super Project> action) {
@@ -477,7 +492,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
     }
 
     public AntBuilder createAntBuilder() {
-        return antBuilderFactory.createAntBuilder();
+        return antBuilderFactory.create();
     }
 
     /**
@@ -509,6 +524,10 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return taskContainer;
     }
 
+    public TaskContainerInternal getImplicitTasks() {
+        return implicitTasksContainer;
+    }
+
     public void defaultTasks(String... defaultTasks) {
         if (defaultTasks == null) {
             throw new InvalidUserDataException("Default tasks must not be null!");
@@ -608,7 +627,7 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
 
     public Project childrenDependOnMe() {
         for (Project project : childProjects.values()) {
-            project.dependsOn(this.path, false);
+            project.dependsOn(getPath(), false);
         }
         return this;
     }
@@ -742,11 +761,11 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         this.taskContainer = taskContainer;
     }
 
-    public AntBuilderFactory getAntBuilderFactory() {
+    public Factory<? extends AntBuilder> getAntBuilderFactory() {
         return antBuilderFactory;
     }
 
-    public void setAntBuilderFactory(AntBuilderFactory antBuilderFactory) {
+    public void setAntBuilderFactory(Factory<? extends AntBuilder> antBuilderFactory) {
         this.antBuilderFactory = antBuilderFactory;
     }
 
@@ -786,7 +805,6 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return loggingManager;
     }
 
-    @Override
     public LoggingManager getLogging() {
         return loggingManager;
     }
@@ -833,12 +851,12 @@ public abstract class AbstractProject implements ProjectInternal, DynamicObjectA
         return fileOperations.exec(closure);
     }
 
-    public ServiceRegistryFactory getServiceRegistryFactory() {
+    public ServiceRegistryFactory getServices() {
         return services;
     }
 
     public Module getModule() {
-        return getServiceRegistryFactory().get(DependencyMetaDataProvider.class).getModule();
+        return getServices().get(DependencyMetaDataProvider.class).getModule();
     }
 
     public void apply(Closure closure) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AntBuilderFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AntBuilderFactory.java
deleted file mode 100644
index 02f3365..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/AntBuilderFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.project;
-
-import org.gradle.api.AntBuilder;
-
-/**
- * @author Hans Dockter
- */
-public interface AntBuilderFactory {
-    AntBuilder createAntBuilder();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java
index cb761c3..44d9c35 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactory.java
@@ -16,12 +16,14 @@
 package org.gradle.api.internal.project;
 
 import org.apache.tools.ant.BuildListener;
+import org.gradle.api.AntBuilder;
 import org.gradle.api.Project;
+import org.gradle.api.internal.Factory;
 
 /**
  * @author Hans Dockter
  */
-public class DefaultAntBuilderFactory implements AntBuilderFactory {
+public class DefaultAntBuilderFactory implements Factory<AntBuilder> {
     private final BuildListener buildListener;
     private final Project project;
 
@@ -30,7 +32,7 @@ public class DefaultAntBuilderFactory implements AntBuilderFactory {
         this.project = project;
     }
 
-    public DefaultAntBuilder createAntBuilder() {
+    public DefaultAntBuilder create() {
         DefaultAntBuilder antBuilder = new DefaultAntBuilder(project);
         antBuilder.getProject().setBaseDir(project.getProjectDir());
         antBuilder.getProject().removeBuildListener((BuildListener) antBuilder.getProject().getBuildListeners().get(0));
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java
index fc7926f..2da01d6 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/DefaultServiceRegistry.java
@@ -15,17 +15,19 @@
  */
 package org.gradle.api.internal.project;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.messaging.concurrent.CompositeStoppable;
 import org.gradle.messaging.concurrent.Stoppable;
 import org.gradle.util.UncheckedException;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.lang.reflect.*;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * A hierarchical {@link ServiceRegistry} implementation. Subclasses can register services by:
+ * A hierarchical {@link ServiceRegistry} implementation.
+ *
+ * <p>Subclasses can register services by:</p>
  *
  * <ul> <li>Calling {@link #add(org.gradle.api.internal.project.DefaultServiceRegistry.Service)} to register a factory
  * for the service.</li>
@@ -35,12 +37,17 @@ import java.util.List;
  * <li>Adding a factory method. A factory method should have a name that starts with 'create', take no parameters, and
  * have a non-void return type. For example, <code>protected SomeService createSomeService() { .... }</code>.</li>
  *
- * <li>Adding a decorator method. A decorator method should have a name that starts with 'decorate', take a single parameter, and a have a non-void return type. 
+ * <li>Adding a decorator method. A decorator method should have a name that starts with 'decorate', take a single
+ * parameter, and a have a non-void return type. The before invoking the method, the parameter is located in the parent
+ * service registry and then passed to the method.</li>
+ *
+ * </ul>
  *
- *  </ul>
+ * <p>Service instances are created on demand. {@link #getFactory(Class)} looks for a service instance which implements
+ * {@code Factory<T>} where {@code T} is the expected type.</p>.
  *
- * <p>Service instances are created on demand. If a service of a given type cannot be located, the registry uses its
- * parent registry, if any, to locate the service.</p>
+ * <p>Service registries are arranged in a heirarchy. If a service of a given type cannot be located, the registry uses
+ * its parent registry, if any, to locate the service.</p>
  */
 public class DefaultServiceRegistry implements ServiceRegistry {
     private final List<Service> services = new ArrayList<Service>();
@@ -134,14 +141,43 @@ public class DefaultServiceRegistry implements ServiceRegistry {
                 serviceType.getSimpleName(), this));
     }
 
+    public <T> Factory<? extends T> getFactory(Class<T> type) {
+        if (closed) {
+            throw new IllegalStateException(String.format("Cannot locate factory for objects of type %s, as %s has been closed.",
+                    type.getSimpleName(), this));
+        }
+
+        for (Service service : services) {
+            Factory<? extends T> factory = service.getFactory(type);
+            if (factory != null) {
+                return factory;
+            }
+        }
+
+        if (parent != null) {
+            try {
+                return parent.getFactory(type);
+            } catch (UnknownServiceException e) {
+                if (!e.type.equals(type)) {
+                    throw e;
+                }
+                // Ignore
+            }
+        }
+
+        throw new UnknownServiceException(type, String.format("No factory for objects of type %s available in %s.",
+                type.getSimpleName(), this));
+    }
+
+    public <T> T newInstance(Class<T> type) {
+        return getFactory(type).create();
+    }
+
     private static Object invoke(Method method, Object target, Object... args) {
         try {
             method.setAccessible(true);
             return method.invoke(target, args);
         } catch (InvocationTargetException e) {
-            if (e.getCause() instanceof RuntimeException) {
-                throw (RuntimeException) e.getCause();
-            }
             throw UncheckedException.asUncheckedException(e.getCause());
         } catch (Exception e) {
             throw UncheckedException.asUncheckedException(e);
@@ -149,15 +185,22 @@ public class DefaultServiceRegistry implements ServiceRegistry {
     }
 
     protected static abstract class Service implements Stoppable {
-        final Class<?> serviceType;
+        final Type serviceType;
+        final Class serviceClass;
         Object service;
 
-        Service(Class<?> serviceType) {
+        Service(Type serviceType) {
             this.serviceType = serviceType;
+            serviceClass = toClass(serviceType);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Service %s", serviceType);
         }
 
         <T> T getService(Class<T> serviceType) {
-            if (!serviceType.isAssignableFrom(this.serviceType)) {
+            if (!serviceType.isAssignableFrom(this.serviceClass)) {
                 return null;
             }
             if (service == null) {
@@ -187,13 +230,52 @@ public class DefaultServiceRegistry implements ServiceRegistry {
                 service = null;
             }
         }
+
+        public <T> Factory<? extends T> getFactory(Class<T> elementType) {
+            if (!Factory.class.isAssignableFrom(serviceClass)) {
+                return null;
+            }
+            return getFactory(serviceType, elementType);
+        }
+
+        private Factory getFactory(Type type, Class elementType) {
+            Class c = toClass(type);
+            if (!Factory.class.isAssignableFrom(c)) {
+                return null;
+            }
+
+            if (type instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType) type;
+                if (parameterizedType.getRawType().equals(Factory.class) && parameterizedType.getActualTypeArguments()[0].equals(elementType)) {
+                    return getService(Factory.class);
+                }
+            }
+
+            for (Type interfaceType : c.getGenericInterfaces()) {
+                Factory f = getFactory(interfaceType, elementType);
+                if (f != null) {
+                    return f;
+                }
+            }
+
+            return null;
+        }
+
+        private Class toClass(Type type) {
+            if (type instanceof Class) {
+                return (Class) type;
+            } else {
+                ParameterizedType parameterizedType = (ParameterizedType) type;
+                return (Class) parameterizedType.getRawType();
+            }
+        }
     }
 
     private class FactoryMethodService extends Service {
         private final Method method;
 
         public FactoryMethodService(Method method) {
-            super(method.getReturnType());
+            super(method.getGenericReturnType());
             this.method = method;
         }
 
@@ -222,18 +304,32 @@ public class DefaultServiceRegistry implements ServiceRegistry {
         private final Method method;
 
         public DecoratorMethodService(Method method) {
-            super(method.getReturnType());
+            super(method.getGenericReturnType());
             this.method = method;
         }
 
         @Override
         protected Object create() {
-            return invoke(method, DefaultServiceRegistry.this, parent.get(method.getParameterTypes()[0]));
+            Object value;
+            if (Factory.class.isAssignableFrom(method.getParameterTypes()[0])) {
+                ParameterizedType fatoryType = (ParameterizedType) method.getGenericParameterTypes()[0];
+                Type typeArg = fatoryType.getActualTypeArguments()[0];
+                Class type;
+                if (typeArg instanceof WildcardType) {
+                    WildcardType wildcardType = (WildcardType) typeArg;
+                    type = (Class) wildcardType.getUpperBounds()[0];
+                } else {
+                    type = (Class) typeArg;
+                }
+                value = parent.getFactory(type);
+            } else {
+                value = parent.get(method.getParameterTypes()[0]);
+            }
+            return invoke(method, DefaultServiceRegistry.this, value);
         }
     }
 
     static class UnknownServiceException extends IllegalArgumentException {
-
         private final Class<?> type;
 
         UnknownServiceException(Class<?> type, String message) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
index 4d7b262..7776877 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GlobalServicesRegistry.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.project;
 
+import org.gradle.StartParameter;
 import org.gradle.api.internal.ClassPathRegistry;
 import org.gradle.api.internal.DefaultClassPathProvider;
 import org.gradle.api.internal.DefaultClassPathRegistry;
@@ -24,25 +25,27 @@ import org.gradle.cache.AutoCloseCacheFactory;
 import org.gradle.cache.CacheFactory;
 import org.gradle.cache.DefaultCacheFactory;
 import org.gradle.initialization.ClassLoaderFactory;
-import org.gradle.initialization.CommandLine2StartParameterConverter;
+import org.gradle.initialization.CommandLineConverter;
 import org.gradle.initialization.DefaultClassLoaderFactory;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
+import org.gradle.initialization.DefaultCommandLineConverter;
 import org.gradle.listener.DefaultListenerManager;
 import org.gradle.listener.ListenerManager;
-import org.gradle.logging.DefaultProgressLoggerFactory;
 import org.gradle.logging.LoggingServiceRegistry;
-import org.gradle.logging.ProgressLoggerFactory;
 
 /**
  * Contains the services shared by all builds in a given process.
  */
 public class GlobalServicesRegistry extends DefaultServiceRegistry {
     public GlobalServicesRegistry() {
-        super(new LoggingServiceRegistry());
+        this(new LoggingServiceRegistry());
     }
 
-    protected CommandLine2StartParameterConverter createCommandLine2StartParameterConverter() {
-        return new DefaultCommandLine2StartParameterConverter();
+    public GlobalServicesRegistry(ServiceRegistry loggingServices) {
+        super(loggingServices);
+    }
+
+    protected CommandLineConverter<StartParameter> createCommandLine2StartParameterConverter() {
+        return new DefaultCommandLineConverter();
     }
 
     protected ClassPathRegistry createClassPathRegistry() {
@@ -60,11 +63,7 @@ public class GlobalServicesRegistry extends DefaultServiceRegistry {
     protected ListenerManager createListenerManager() {
         return new DefaultListenerManager();
     }
-
-    protected ProgressLoggerFactory createProgressLoggerFactory() {
-        return new DefaultProgressLoggerFactory(get(ListenerManager.class));
-    }
-    
+   
     protected GradleDistributionLocator createGradleDistributionLocator() {
         return new DefaultClassPathProvider();
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java
index bed26be..30100d5 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistry.java
@@ -16,11 +16,11 @@
 package org.gradle.api.internal.project;
 
 import org.gradle.api.Project;
-import org.gradle.api.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.PublishModuleDescriptorConverter;
 import org.gradle.api.internal.artifacts.repositories.DefaultInternalRepository;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.plugins.DefaultPluginRegistry;
 import org.gradle.api.internal.plugins.PluginRegistry;
 import org.gradle.execution.DefaultTaskGraphExecuter;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java
index f3e05f6..5f9bd28 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectFactory.java
@@ -54,7 +54,7 @@ public class ProjectFactory implements IProjectFactory {
                 projectDescriptor.getProjectDir(),
                 source,
                 gradle,
-                gradle.getServiceRegistryFactory());
+                gradle.getServices());
 
         if (parent != null) {
             parent.addChildProject(project);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java
index 6e00577..0a17a88 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternal.java
@@ -25,17 +25,21 @@ import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.file.FileOperations;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
-import org.gradle.logging.StandardOutputCapture;
 import org.gradle.groovy.scripts.ScriptAware;
 import org.gradle.groovy.scripts.ScriptSource;
+import org.gradle.logging.StandardOutputCapture;
 
 public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware, FileOperations, DomainObjectContext {
     ProjectInternal getParent();
 
+    ProjectInternal getRootProject();
+
     Project evaluate();
 
     TaskContainerInternal getTasks();
 
+    TaskContainerInternal getImplicitTasks();
+
     ScriptSource getBuildScriptSource();
 
     void addChildProject(ProjectInternal childProject);
@@ -50,7 +54,7 @@ public interface ProjectInternal extends Project, ProjectIdentifier, ScriptAware
 
     FileResolver getFileResolver();
 
-    ServiceRegistryFactory getServiceRegistryFactory();
+    ServiceRegistryFactory getServices();
 
     Module getModule();
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
index 5d9c399..1b9ef0d 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistry.java
@@ -16,25 +16,26 @@
 
 package org.gradle.api.internal.project;
 
+import org.gradle.api.AntBuilder;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.dsl.ArtifactHandler;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
-import org.gradle.api.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.ClassGenerator;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory;
 import org.gradle.api.internal.artifacts.DefaultModule;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
-import org.gradle.api.internal.artifacts.configurations.ResolverProvider;
 import org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler;
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactFactory;
+import org.gradle.api.internal.artifacts.dsl.SharedConventionRepositoryHandlerFactory;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
 import org.gradle.api.internal.artifacts.dsl.dependencies.ProjectFinder;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.file.*;
 import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
@@ -44,12 +45,10 @@ import org.gradle.api.internal.plugins.DefaultProjectsPluginContainer;
 import org.gradle.api.internal.plugins.PluginRegistry;
 import org.gradle.api.internal.project.ant.AntLoggingAdapter;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
-import org.gradle.api.internal.tasks.DefaultTaskContainer;
+import org.gradle.api.internal.tasks.DefaultTaskContainerFactory;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
-import org.gradle.api.internal.tasks.TaskResolver;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.PluginContainer;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 
 import java.io.File;
@@ -74,11 +73,11 @@ public class ProjectInternalServiceRegistry extends DefaultServiceRegistry imple
     }
 
     protected LoggingManagerInternal createLoggingManager() {
-        return get(LoggingManagerFactory.class).create();
+        return getFactory(LoggingManagerInternal.class).create();
     }
 
     protected FileOperations createFileOperations() {
-        return new DefaultFileOperations(get(FileResolver.class), get(TaskResolver.class), get(TemporaryFileProvider.class));
+        return new DefaultFileOperations(get(FileResolver.class), project.getTasks(), get(TemporaryFileProvider.class));
     }
 
     protected TemporaryFileProvider createTemporaryFileProvider() {
@@ -89,7 +88,7 @@ public class ProjectInternalServiceRegistry extends DefaultServiceRegistry imple
         });
     }
 
-    protected AntBuilderFactory createAntBuilderFactory() {
+    protected Factory<AntBuilder> createAntBuilderFactory() {
         return new DefaultAntBuilderFactory(new AntLoggingAdapter(), project);
     }
 
@@ -97,21 +96,24 @@ public class ProjectInternalServiceRegistry extends DefaultServiceRegistry imple
         return new DefaultProjectsPluginContainer(get(PluginRegistry.class), project);
     }
 
-    protected TaskContainerInternal createTaskContainerInternal() {
-        ClassGenerator classGenerator = get(ClassGenerator.class);
-        return classGenerator.newInstance(DefaultTaskContainer.class, project, classGenerator, get(ITaskFactory.class));
+    protected Factory<TaskContainerInternal> createTaskContainerInternal() {
+        return new DefaultTaskContainerFactory(get(ClassGenerator.class), get(ITaskFactory.class), project);
     }
 
     protected Convention createConvention() {
         return new DefaultConvention();
     }
 
-    protected RepositoryHandler createRepositoryHandler() {
-        return get(RepositoryHandlerFactory.class).createRepositoryHandler(get(Convention.class));
+    protected Factory<RepositoryHandler> createRepositoryHandlerFactory(Factory<? extends RepositoryHandler> factory) {
+        return new SharedConventionRepositoryHandlerFactory(factory, get(Convention.class));
     }
 
+    protected RepositoryHandler createRepositoryHandler() {
+        return getFactory(RepositoryHandler.class).create();
+    }
+    
     protected ConfigurationContainer createConfigurationContainer() {
-        return get(ConfigurationContainerFactory.class).createConfigurationContainer(get(ResolverProvider.class),
+        return get(ConfigurationContainerFactory.class).createConfigurationContainer(project.getRepositories(),
                 get(DependencyMetaDataProvider.class), project);
     }
 
@@ -134,7 +136,7 @@ public class ProjectInternalServiceRegistry extends DefaultServiceRegistry imple
 
     protected ScriptHandlerInternal createScriptHandler() {
         DefaultScriptHandlerFactory factory = new DefaultScriptHandlerFactory(
-                get(RepositoryHandlerFactory.class),
+                getFactory(RepositoryHandler.class),
                 get(ConfigurationContainerFactory.class),
                 get(DependencyMetaDataProvider.class),
                 get(DependencyFactory.class));
@@ -169,4 +171,5 @@ public class ProjectInternalServiceRegistry extends DefaultServiceRegistry imple
         }
         throw new UnsupportedOperationException();
     }
+
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java
index 757223d..0e07154 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/ServiceRegistry.java
@@ -15,7 +15,39 @@
  */
 package org.gradle.api.internal.project;
 
-public interface ServiceRegistry
-{
+import org.gradle.api.internal.Factory;
+
+/**
+ * A registry of services.
+ */
+public interface ServiceRegistry {
+    /**
+     * Locates the service of the given type. There is a single instance for each service type.
+     *
+     * @param serviceType The service type.
+     * @param <T>         The service type.
+     * @return The service instance. Never returns null.
+     * @throws IllegalArgumentException When there is no service of the given type available.
+     */
     <T> T get(Class<T> serviceType) throws IllegalArgumentException;
+
+    /**
+     * Locates a factory which can create services of the given type.
+     *
+     * @param type The service type that the factory should create.
+     * @param <T>  The service type that the factory should create.
+     * @return The factory. Never returns null.
+     * @throws IllegalArgumentException When there is no factory available for services of the given type.
+     */
+    <T> Factory<? extends T> getFactory(Class<T> type) throws IllegalArgumentException;
+
+    /**
+     * Creates a new service instance of the given type.
+     *
+     * @param type The service type
+     * @param <T>  The service type.
+     * @return The instance. Never returns null.
+     * @throws IllegalArgumentException When there is no factory available for services of the given type.
+     */
+    <T> T newInstance(Class<T> type) throws IllegalArgumentException;
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
index be6a4bf..4adbd39 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistry.java
@@ -21,7 +21,6 @@ import org.gradle.api.internal.TaskOutputsInternal;
 import org.gradle.api.internal.tasks.DefaultTaskInputs;
 import org.gradle.api.internal.tasks.DefaultTaskOutputs;
 import org.gradle.api.tasks.TaskInputs;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 
 /**
@@ -46,7 +45,7 @@ public class TaskInternalServiceRegistry extends DefaultServiceRegistry implemen
     }
 
     protected LoggingManagerInternal createLoggingManager() {
-        return get(LoggingManagerFactory.class).create();
+        return getFactory(LoggingManagerInternal.class).create();
     }
 
     public ServiceRegistryFactory createFor(Object domainObject) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java
index 2228045..e9218f5 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistry.java
@@ -21,8 +21,7 @@ import org.apache.ivy.plugins.resolver.ChainResolver;
 import org.gradle.StartParameter;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
-import org.gradle.api.artifacts.repositories.InternalRepository;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.execution.TaskActionListener;
 import org.gradle.api.internal.*;
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory;
@@ -36,6 +35,7 @@ import org.gradle.api.internal.artifacts.dsl.dependencies.*;
 import org.gradle.api.internal.artifacts.ivyservice.*;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.*;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.*;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.changedetection.*;
 import org.gradle.api.internal.file.IdentityFileResolver;
 import org.gradle.api.internal.initialization.DefaultScriptHandlerFactory;
@@ -53,16 +53,16 @@ import org.gradle.configuration.*;
 import org.gradle.groovy.scripts.*;
 import org.gradle.initialization.*;
 import org.gradle.listener.ListenerManager;
-import org.gradle.logging.LoggingManagerFactory;
+import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.messaging.actor.ActorFactory;
+import org.gradle.messaging.actor.internal.DefaultActorFactory;
 import org.gradle.messaging.concurrent.DefaultExecutorFactory;
 import org.gradle.messaging.concurrent.ExecutorFactory;
 import org.gradle.messaging.remote.MessagingServer;
 import org.gradle.messaging.remote.internal.TcpMessagingServer;
-import org.gradle.messaging.actor.ActorFactory;
-import org.gradle.messaging.actor.internal.DefaultActorFactory;
 import org.gradle.process.internal.DefaultWorkerProcessFactory;
-import org.gradle.process.internal.WorkerProcessFactory;
+import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.process.internal.child.WorkerProcessClassPathProvider;
 import org.gradle.util.*;
 
@@ -134,10 +134,10 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
                                         get(TaskArtifactStateRepository.class))));
     }
 
-    protected RepositoryHandlerFactory createRepositoryHandlerFactory() {
+    protected Factory<RepositoryHandler> createRepositoryHandlerFactory() {
         return new DefaultRepositoryHandlerFactory(
                 new DefaultResolverFactory(
-                        get(LoggingManagerFactory.class)),
+                        getFactory(LoggingManagerInternal.class)),
                 get(ClassGenerator.class));
     }
 
@@ -162,13 +162,19 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
         return new DefaultConfigurationsToModuleDescriptorConverter();
     }
     
-    protected ResolveModuleDescriptorConverter createResolveModuleDescriptorConverter() {
-        return createResolveModuleDescriptorConverter(ProjectDependencyDescriptorFactory.RESOLVE_DESCRIPTOR_STRATEGY);
+    private ResolveModuleDescriptorConverter createResolveModuleDescriptorConverter(ProjectDependencyDescriptorStrategy projectDependencyStrategy) {
+        DependencyDescriptorFactory dependencyDescriptorFactoryDelegate = createDependencyDescriptorFactory(projectDependencyStrategy);
+        return new ResolveModuleDescriptorConverter(
+                get(ModuleDescriptorFactory.class),
+                get(ConfigurationsToModuleDescriptorConverter.class),
+                new DefaultDependenciesToModuleDescriptorConverter(
+                        dependencyDescriptorFactoryDelegate,
+                        get(ExcludeRuleConverter.class)));
     }
 
-    protected ResolveModuleDescriptorConverter createResolveModuleDescriptorConverter(ProjectDependencyDescriptorStrategy projectDependencyStrategy) {
+    private DependencyDescriptorFactory createDependencyDescriptorFactory(ProjectDependencyDescriptorStrategy projectDependencyStrategy) {
         DefaultModuleDescriptorFactoryForClientModule clientModuleDescriptorFactory = new DefaultModuleDescriptorFactoryForClientModule();
-        DependencyDescriptorFactoryDelegate dependencyDescriptorFactoryDelegate = new DependencyDescriptorFactoryDelegate(
+        DependencyDescriptorFactory dependencyDescriptorFactoryDelegate = new DependencyDescriptorFactoryDelegate(
                 new ClientModuleDependencyDescriptorFactory(
                         get(ExcludeRuleConverter.class), clientModuleDescriptorFactory, clientModuleRegistry),
                 new ProjectDependencyDescriptorFactory(
@@ -176,12 +182,7 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
                         projectDependencyStrategy),
                 get(ExternalModuleDependencyDescriptorFactory.class));
         clientModuleDescriptorFactory.setDependencyDescriptorFactory(dependencyDescriptorFactoryDelegate);
-        return new ResolveModuleDescriptorConverter(
-                get(ModuleDescriptorFactory.class),
-                get(ConfigurationsToModuleDescriptorConverter.class),
-                new DefaultDependenciesToModuleDescriptorConverter(
-                        dependencyDescriptorFactoryDelegate,
-                        get(ExcludeRuleConverter.class)));
+        return dependencyDescriptorFactoryDelegate;
     }
 
     protected PublishModuleDescriptorConverter createPublishModuleDescriptorConverter() {
@@ -191,17 +192,7 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
     }
 
     protected ConfigurationContainerFactory createConfigurationContainerFactory() {
-        // todo this creation is duplicate. When we improve our service registry to allow multiple instances for same type
-        // we should consolidate.
-        DefaultModuleDescriptorFactoryForClientModule clientModuleDescriptorFactory = new DefaultModuleDescriptorFactoryForClientModule();
-        DependencyDescriptorFactoryDelegate dependencyDescriptorFactoryDelegate = new DependencyDescriptorFactoryDelegate(
-                new ClientModuleDependencyDescriptorFactory(
-                        get(ExcludeRuleConverter.class), clientModuleDescriptorFactory, clientModuleRegistry),
-                new ProjectDependencyDescriptorFactory(
-                        get(ExcludeRuleConverter.class),
-                        ProjectDependencyDescriptorFactory.RESOLVE_DESCRIPTOR_STRATEGY),
-                get(ExternalModuleDependencyDescriptorFactory.class));
-        clientModuleDescriptorFactory.setDependencyDescriptorFactory(dependencyDescriptorFactoryDelegate);
+        DependencyDescriptorFactory dependencyDescriptorFactoryDelegate = createDependencyDescriptorFactory(ProjectDependencyDescriptorFactory.RESOLVE_DESCRIPTOR_STRATEGY);
         PublishModuleDescriptorConverter fileModuleDescriptorConverter = new PublishModuleDescriptorConverter(
                 createResolveModuleDescriptorConverter(ProjectDependencyDescriptorFactory.IVY_FILE_DESCRIPTOR_STRATEGY),
                 new DefaultArtifactsToModuleDescriptorConverter(DefaultArtifactsToModuleDescriptorConverter.IVY_FILE_STRATEGY));
@@ -210,7 +201,7 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
                 new DefaultSettingsConverter(
                         get(ProgressLoggerFactory.class)
                 ),
-                get(ResolveModuleDescriptorConverter.class),
+                get(PublishModuleDescriptorConverter.class),
                 get(PublishModuleDescriptorConverter.class),
                 fileModuleDescriptorConverter,
                 new DefaultIvyFactory(),
@@ -286,7 +277,7 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
                 get(ImportsReader.class),
                 get(ScriptHandlerFactory.class),
                 get(ClassLoader.class),
-                get(LoggingManagerFactory.class));
+                getFactory(LoggingManagerInternal.class));
     }
 
     protected MultiParentClassLoader createRootClassLoader() {
@@ -316,13 +307,13 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
 
     protected ScriptHandlerFactory createScriptHandlerFactory() {
         return new DefaultScriptHandlerFactory(
-                get(RepositoryHandlerFactory.class),
+                getFactory(RepositoryHandler.class),
                 get(ConfigurationContainerFactory.class),
                 new DependencyMetaDataProviderImpl(), 
                 get(DependencyFactory.class));
     }
 
-    protected WorkerProcessFactory createWorkerProcessFactory() {
+    protected Factory<WorkerProcessBuilder> createWorkerProcessFactory() {
         ClassPathRegistry classPathRegistry = get(ClassPathRegistry.class);
         return new DefaultWorkerProcessFactory(startParameter.getLogLevel(), get(MessagingServer.class), classPathRegistry,
                 new IdentityFileResolver(), new LongIdGenerator());
@@ -331,7 +322,14 @@ public class TopLevelBuildServiceRegistry extends DefaultServiceRegistry impleme
     protected MessagingServer createMessagingServer() {
         return new TcpMessagingServer(get(ClassLoaderFactory.class).getRootClassLoader());
     }
-    
+
+    protected BuildConfigurer createBuildConfigurer() {
+        return new DefaultBuildConfigurer(
+                new ProjectEvaluationConfigurer(),
+                new ProjectDependencies2TaskResolver(),
+                new ImplicitTasksConfigurer());
+    }
+
     public ServiceRegistryFactory createFor(Object domainObject) {
         if (domainObject instanceof GradleInternal) {
             return new GradleInternalServiceRegistry(this, (GradleInternal) domainObject);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java
index c1e0bf0..240099e 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/project/taskfactory/NestedBeanPropertyAnnotationHandler.java
@@ -32,7 +32,6 @@ public class NestedBeanPropertyAnnotationHandler implements PropertyAnnotationHa
         context.setConfigureAction(new UpdateAction() {
             public void update(Task task, final Callable<Object> futureValue) {
                 task.getInputs().property(context.getName() + ".class", new Callable<Object>() {
-                    @Override
                     public Object call() throws Exception {
                         Object bean = futureValue.call();
                         return bean == null ? null : bean.getClass().getName();
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/CommandLineOption.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/CommandLineOption.java
new file mode 100644
index 0000000..c68b1d5
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/CommandLineOption.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks;
+
+import java.lang.annotation.*;
+
+/**
+ * Marks a property as available from the command-line.
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+ at Inherited
+public @interface CommandLineOption {
+    /**
+     * The command-line options to map to this property.
+     *
+     * @return The command-line options.
+     */
+    String[] options();
+
+    /**
+     * The description of this command-line option.
+     *
+     * @return The description.
+     */
+    String description();
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java
index 12be9cd..0fb1086 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainer.java
@@ -22,8 +22,8 @@ import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.UnknownTaskException;
 import org.gradle.api.internal.ClassGenerator;
-import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 import org.gradle.util.GUtil;
 
 import java.util.HashMap;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
new file mode 100644
index 0000000..4ad0be0
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks;
+
+import org.gradle.api.Project;
+import org.gradle.api.internal.ClassGenerator;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+
+public class DefaultTaskContainerFactory implements Factory<TaskContainerInternal> {
+    private final ClassGenerator classGenerator;
+    private final ITaskFactory taskFactory;
+    private Project project;
+
+    public DefaultTaskContainerFactory(ClassGenerator classGenerator, ITaskFactory taskFactory, Project project) {
+        this.classGenerator = classGenerator;
+        this.taskFactory = taskFactory;
+        this.project = project;
+    }
+
+    public TaskContainerInternal create() {
+        return classGenerator.newInstance(DefaultTaskContainer.class, project, classGenerator, taskFactory);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java
index 41a7e21..adb3e5a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskExecuter.java
@@ -36,12 +36,15 @@ public class DefaultTaskExecuter implements TaskExecuter {
 
     public void execute(TaskInternal task, TaskStateInternal state) {
         listener.beforeActions(task);
+        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(task.getClass().getClassLoader());
         state.setExecuting(true);
         try {
             GradleException failure = executeActions(task, state);
             state.executed(failure);
         } finally {
             state.setExecuting(false);
+            Thread.currentThread().setContextClassLoader(originalClassLoader);
             listener.afterActions(task);
         }
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
index 110bfce..0a9f141 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskInputs.java
@@ -49,6 +49,11 @@ public class DefaultTaskInputs implements TaskInputs {
         return this;
     }
 
+    public TaskInputs file(Object path) {
+        files(path);
+        return this;
+    }
+
     public TaskInputs dir(Object dirPath) {
         inputFiles.from(resolver.resolveFilesAsTree(dirPath));
         return this;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
index 6f7b4db..5d1f65c 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
@@ -63,6 +63,11 @@ public class DefaultTaskOutputs implements TaskOutputsInternal {
         return this;
     }
 
+    public TaskOutputs file(Object path) {
+        files(path);
+        return this;
+    }
+
     public TaskOutputs dir(Object path) {
         files(path);
         return this;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
index 01b7470..2047dbc 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/internal/tasks/TaskStateInternal.java
@@ -46,7 +46,7 @@ public class TaskStateInternal implements TaskState {
     }
 
     /**
-     * Marks this task as executed.
+     * Marks this task as executed with no failure.
      */
     public void executed() {
         this.executed = true;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LogLevel.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LogLevel.java
index b0a1b6e..4107c32 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LogLevel.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LogLevel.java
@@ -16,6 +16,8 @@
 package org.gradle.api.logging;
 
 /**
+ * The log levels supported by Gradle.
+ *
  * @author Hans Dockter
  */
 public enum LogLevel {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/Logging.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/Logging.java
index 9e551f8..0d60dc2 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/Logging.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/Logging.java
@@ -17,9 +17,9 @@
 package org.gradle.api.logging;
 
 import org.apache.ivy.util.Message;
-import org.slf4j.MarkerFactory;
-import org.slf4j.Marker;
 import org.slf4j.LoggerFactory;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -32,16 +32,8 @@ import java.util.Map;
  */
 public class Logging {
     public static final Marker LIFECYCLE = MarkerFactory.getDetachedMarker("LIFECYCLE");
-    public static final Marker PROGRESS = MarkerFactory.getDetachedMarker("PROGRESS");
-    public static final Marker PROGRESS_STARTED = MarkerFactory.getDetachedMarker("PROGRESS_START");
-    public static final Marker PROGRESS_COMPLETE = MarkerFactory.getDetachedMarker("PROGRESS_COMPLETE");
     public static final Marker QUIET = MarkerFactory.getDetachedMarker("QUIET");
 
-    static {
-        PROGRESS_STARTED.add(PROGRESS);
-        PROGRESS_COMPLETE.add(PROGRESS);
-    }
-    
     /**
      * Returns the logger for the given class.
      *
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java
index 6ea5845..a5b50ee 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/logging/LoggingOutput.java
@@ -28,7 +28,7 @@ public interface LoggingOutput {
     void addStandardOutputListener(StandardOutputListener listener);
 
     /**
-     * Removes a listener on standard output
+     * Removes a listener from standard output.
      *
      * @param listener The listener to remove.
      */
@@ -42,7 +42,7 @@ public interface LoggingOutput {
     void addStandardErrorListener(StandardOutputListener listener);
 
     /**
-     * Removes a listener on standard error
+     * Removes a listener from standard error.
      *
      * @param listener The listener to remove.
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java
index 0a1ca3d..ff9f550 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginCollection.java
@@ -26,6 +26,7 @@ import groovy.lang.Closure;
  * <p>A {@code PluginCollection} represents a collection of {@link org.gradle.api.Plugin} instances.</p>
  * 
  * @author Hans Dockter
+ * @param <T> The type of plugins which this collection contains.
  */
 public interface PluginCollection<T extends Plugin> extends DomainObjectCollection<T> {
     /**
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java
index e93ed3e..914d3fd 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/PluginInstantiationException.java
@@ -18,6 +18,8 @@ package org.gradle.api.plugins;
 import org.gradle.api.GradleException;
 
 /**
+ * A {@code PluginInstantiationException} is thrown when a plugin cannot be instantiated.
+ *
  * @author Hans Dockter
  */
 public class PluginInstantiationException extends GradleException {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java
index 0af007b..fce0920 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/plugins/UnknownPluginException.java
@@ -18,6 +18,8 @@ package org.gradle.api.plugins;
 import org.gradle.api.InvalidUserDataException;
 
 /**
+ * A {@code UnknownPluginException} is thrown when an unknown plugin id is provided. 
+ *
  * @author Hans Dockter
  */
 public class UnknownPluginException extends InvalidUserDataException {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/AndSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/AndSpec.java
index d21da61..b1745a8 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/AndSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/AndSpec.java
@@ -21,7 +21,11 @@ import org.gradle.util.GUtil;
 import java.util.Arrays;
 
 /**
+ * A {@link org.gradle.api.specs.CompositeSpec} which requires all its specs to be true in order to evaluate to true.
+ * Uses lazy evaluation.
+ *
  * @author Hans Dockter
+ * @param <T> The target type for this Spec
  */
 public class AndSpec<T> extends CompositeSpec<T> {
     public AndSpec(Spec<? super T>... specs) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/CompositeSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/CompositeSpec.java
index ae0b61b..a43ace9 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/CompositeSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/CompositeSpec.java
@@ -22,7 +22,10 @@ import java.util.Collections;
 import java.util.List;
 
 /**
+ * A {@link org.gradle.api.specs.Spec} which aggregates a sequence of other {@code Spec} instances.
+ *
  * @author Hans Dockter
+ * @param <T> The target type for this Spec
  */
 abstract public class CompositeSpec<T> implements Spec<T> {
     private List<Spec<? super T>> specs;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/NotSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/NotSpec.java
index ce41c55..1e2ac91 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/NotSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/NotSpec.java
@@ -16,7 +16,10 @@
 package org.gradle.api.specs;
 
 /**
+ * A {@link Spec} implementation which negates another {@code Spec}.
+ * 
  * @author Hans Dockter
+ * @param <T> The target type for this Spec
  */
 public class NotSpec<T> implements Spec<T> {
     private Spec<? super T> sourceSpec;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/OrSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/OrSpec.java
index 43b4cac..e108522 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/OrSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/OrSpec.java
@@ -18,7 +18,11 @@ package org.gradle.api.specs;
 import java.util.List;
 
 /**
+ * A {@link org.gradle.api.specs.CompositeSpec} which requires any one of its specs to be true in order to evaluate to
+ * true. Uses lazy evaluation.
+ *
  * @author Hans Dockter
+ * @param <T> The target type for this Spec
  */
 public class OrSpec<T> extends CompositeSpec<T> {
     public OrSpec(Spec<? super T>... specs) {
@@ -38,6 +42,4 @@ public class OrSpec<T> extends CompositeSpec<T> {
         }
         return false;
     }
-
-    
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Spec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Spec.java
index e4d5155..a6522b4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Spec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Spec.java
@@ -16,7 +16,10 @@
 package org.gradle.api.specs;
 
 /**
+ * Represents some predicate against objects of type T.
+ *
  * @author Hans Dockter
+ * @param <T> The target type for this Spec
  */
 public interface Spec<T> {
     boolean isSatisfiedBy(T element);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Specs.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Specs.java
index 8cff137..b349346 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Specs.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/specs/Specs.java
@@ -22,6 +22,8 @@ import java.util.List;
 import java.util.Set;
 
 /**
+ * Provides a number of {@link Spec} implementations.
+ *
  * @author Hans Dockter
  */
 public class Specs {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java
index 1c61f01..e619e09 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/AbstractCopyTask.java
@@ -32,7 +32,7 @@ import java.util.regex.Pattern;
 /**
  * {@code AbstractCopyTask} is the base class for all copy tasks.
  */
-public abstract class AbstractCopyTask extends ConventionTask implements CopyAction, CopySpecSource {
+public abstract class AbstractCopyTask extends ConventionTask implements CopySpec, CopySpecSource {
 
     @TaskAction
     protected void copy() {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Copy.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Copy.java
index 5ae9ad6..4ee30e9 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Copy.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Copy.java
@@ -23,7 +23,7 @@ import org.gradle.api.internal.file.copy.FileCopySpecVisitor;
 import java.io.File;
 
 /**
- * Task for copying files.  This task can also rename and filter files as it copies.
+ * Copies files into a destination directory.  This task can also rename and filter files as it copies.
  * The task implements {@link org.gradle.api.file.CopySpec CopySpec} for specifying
  * what to copy.
  * <p>
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Delete.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Delete.java
index 44832f8..5d9d49d 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Delete.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Delete.java
@@ -23,7 +23,7 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 
 /**
- * <p>Deletes the specified target files or directories.</p>
+ * <p>Deletes files or directories.</p>
  *
  * @author Hans Dockter
  */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Directory.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Directory.groovy
index 176aa16..78cc54b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Directory.groovy
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Directory.groovy
@@ -20,6 +20,8 @@ import org.gradle.api.DefaultTask
 import org.gradle.api.InvalidUserDataException
 
 /**
+ * Creates a directory.
+ *
  * @author Hans Dockter
  */
 public class Directory extends DefaultTask {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Exec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Exec.java
index 831b29c..a09a73a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Exec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Exec.java
@@ -30,7 +30,7 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * A task for executing a command line process.
+ * Executes a command line process.
  * 
  * @author Hans Dockter
  */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java
index 729f695..11ee553 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/GradleBuild.java
@@ -24,7 +24,7 @@ import java.util.Collection;
 import java.util.List;
 
 /**
- * A {@code GradleBuild} task executes another Gradle build.
+ * Executes a Gradle build.
  */
 public class GradleBuild extends ConventionTask {
     private StartParameter startParameter;
@@ -79,7 +79,7 @@ public class GradleBuild extends ConventionTask {
     }
 
     /**
-     * Sets the build file that should be used for this build
+     * Sets the build file that should be used for this build.
      *
      * @param file The build file. May be null to use the default build file for the build.
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/JavaExec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/JavaExec.java
index 06f3683..a5ccb64 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/JavaExec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/JavaExec.java
@@ -32,7 +32,7 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * A task for executing a Java main class.
+ * Executes a Java application.
  *
  * @author Hans Dockter
  */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Sync.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Sync.java
index 0139249..cd9583a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Sync.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Sync.java
@@ -24,8 +24,7 @@ import org.gradle.api.internal.file.copy.SyncCopySpecVisitor;
 import java.io.File;
 
 /**
- * Task for synchronizing the contents of a directory.
- *
+ * Synchronises the contents of a destination directory with some source.
  */
 public class Sync extends AbstractCopyTask {
     private FileCopyActionImpl action;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskCollection.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskCollection.java
index 5cc0428..9bf42c4 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskCollection.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskCollection.java
@@ -21,6 +21,8 @@ import org.gradle.api.specs.Spec;
 
 /**
  * A {@code TaskCollection} contains a set of {@link Task} instances, and provides a number of query methods.
+ *
+ * @param <T> The type of tasks which this collection contains.
  */
 public interface TaskCollection<T extends Task> extends NamedDomainObjectCollection<T> {
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java
index 96a4832..adfae80 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskInputs.java
@@ -49,6 +49,14 @@ public interface TaskInputs {
     TaskInputs files(Object... paths);
 
     /**
+     * Registers some input file for this task.
+     *
+     * @param path The input file. The given path is evaluated as for {@link org.gradle.api.Project#file(Object)}.
+     * @return this
+     */
+    TaskInputs file(Object path);
+
+    /**
      * Registers an input directory hierarchy. All files found under the given directory are treated as input files for
      * this task.
      *
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java
index 9a91f7b..3ad9fff 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/TaskOutputs.java
@@ -67,7 +67,7 @@ public interface TaskOutputs {
     FileCollection getFiles();
 
     /**
-     * Registers some output files/directories for this task.
+     * Registers some output files for this task.
      *
      * @param paths The output files. The given paths are evaluated as for {@link org.gradle.api.Project#files(Object...)}.
      * @return this
@@ -75,6 +75,14 @@ public interface TaskOutputs {
     TaskOutputs files(Object... paths);
 
     /**
+     * Registers some output file for this task.
+     *
+     * @param path The output file. The given path is evaluated as for {@link org.gradle.api.Project#file(Object)}.
+     * @return this
+     */
+    TaskOutputs file(Object path);
+
+    /**
      * Registers an output directory for this task.
      *
      * @param path The output directory. The given path is evaluated as for {@link org.gradle.api.Project#file(Object)}.
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Upload.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Upload.java
index aee34a8..02d91c1 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Upload.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/Upload.java
@@ -28,8 +28,7 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 
 /**
- * An upload task uploads files to the repositories assigned to it.  The files that get uploaded are the artifacts
- * of your project, if they belong to the configuration associated with the upload task.
+ * Uploads the artifacts of a configuration to a set of repositories.
  * 
  * @author Hans Dockter
  */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/WorkResult.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/WorkResult.java
index e44eef4..a4d1e22 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/WorkResult.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/WorkResult.java
@@ -15,6 +15,9 @@
  */
 package org.gradle.api.tasks;
 
+/**
+ * Provides information about some work which was performed.
+ */
 public interface WorkResult {
     public boolean getDidWork();
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java
index eacf432..3618ddf 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/AbstractArchiveTask.java
@@ -135,7 +135,7 @@ public abstract class AbstractArchiveTask extends AbstractCopyTask {
     }
 
     /**
-     * Returns the extension part of the archive name
+     * Returns the extension part of the archive name.
      */
     public String getExtension() {
         return extension;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java
index b5acb29..629322e 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Compression.java
@@ -16,6 +16,8 @@
 package org.gradle.api.tasks.bundling;
 
 /**
+ * Specifies the compression which should be applied to a TAR archive.
+ * 
  * @author Hans Dockter
  */
 public enum Compression {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/LongFile.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/LongFile.java
deleted file mode 100644
index c3db065..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/LongFile.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2007-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.bundling;
-
-/**
- * @author Hans Dockter
- */
-public class LongFile {
-    public static final LongFile TRUNCATE = new LongFile("truncate");
-    public static final LongFile WARN = new LongFile("warn");
-    public static final LongFile GNU = new LongFile("gnu");
-    public static final LongFile OMIT = new LongFile("omit");
-    public static final LongFile FAIL = new LongFile("fail");
-
-    private final String antValue; 
-
-    private LongFile(String name) {
-        antValue = name;
-    }
-
-    public String getAntValue() {
-        return antValue;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java
index 9778b38..fb522c3 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Tar.java
@@ -25,6 +25,8 @@ import java.io.File;
 import java.util.concurrent.Callable;
 
 /**
+ * Assembles a TAR archive.
+ *
  * @author Hans Dockter
  */
 public class Tar extends AbstractArchiveTask {
@@ -32,11 +34,8 @@ public class Tar extends AbstractArchiveTask {
 
     private Compression compression;
 
-    private LongFile longFile;
-
     public Tar() {
         compression = Compression.NONE;
-        longFile = LongFile.WARN;
         action = new TarCopyActionImpl(getServices().get(FileResolver.class));
         getConventionMapping().map("extension", new Callable<Object>(){
             public Object call() throws Exception {
@@ -49,22 +48,22 @@ public class Tar extends AbstractArchiveTask {
         return action;
     }
 
+    /**
+     * Returns the compression to use for this archive. The default is {@link org.gradle.api.tasks.bundling.Compression#NONE}.
+     * @return The compression. Never returns null.
+     */
     public Compression getCompression() {
         return compression;
     }
 
+    /**
+     * Specifies the compression to use for this archive.
+     * @param compression The compression. Should not be null.
+     */
     public void setCompression(Compression compression) {
         this.compression = compression;
     }
 
-    public LongFile getLongFile() {
-        return longFile;
-    }
-
-    public void setLongFile(LongFile longFile) {
-        this.longFile = longFile;
-    }
-
     private class TarCopyActionImpl extends CopyActionImpl implements TarCopyAction {
         public TarCopyActionImpl(FileResolver fileResolver) {
             super(fileResolver, new TarCopySpecVisitor());
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java
index ad4ed8e..b5a5216 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/bundling/Zip.java
@@ -23,6 +23,8 @@ import org.gradle.api.internal.file.copy.CopyActionImpl;
 import java.io.File;
 
 /**
+ * Assembles a ZIP archive.
+ * 
  * @author Hans Dockter
  */
 public class Zip extends AbstractArchiveTask {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java
index 54f66c8..07ac946 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTask.java
@@ -23,9 +23,12 @@ import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.Optional;
 import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
+import org.gradle.logging.StyledTextOutputFactory;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -44,15 +47,19 @@ public abstract class AbstractReportTask extends ConventionTask {
                 return getOutputFile() != null;
             }
         });
+        projects = new HashSet<Project>();
+        projects.add(getProject());
     }
 
     @TaskAction
     public void generate() {
         try {
-            ProjectReportRenderer renderer = getRenderer();
+            ReportRenderer renderer = getRenderer();
             File outputFile = getOutputFile();
             if (outputFile != null) {
                 renderer.setOutputFile(outputFile);
+            } else {
+                renderer.setOutput(getServices().get(StyledTextOutputFactory.class).create(getClass()));
             }
             Set<Project> projects = new TreeSet<Project>(getProjects());
             for (Project project : projects) {
@@ -66,7 +73,7 @@ public abstract class AbstractReportTask extends ConventionTask {
         }
     }
 
-    protected abstract ProjectReportRenderer getRenderer();
+    protected abstract ReportRenderer getRenderer();
 
     protected abstract void generate(Project project) throws IOException;
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AsciiReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AsciiReportRenderer.java
deleted file mode 100644
index a5070c8..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/AsciiReportRenderer.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.artifacts.ResolvedConfiguration;
-import org.gradle.api.artifacts.ResolvedDependency;
-import org.gradle.util.GUtil;
-
-import java.io.IOException;
-import java.util.*;
-
-/**
- * Simple dependency graph renderer that emits an ASCII tree.
- *
- * @author Phil Messenger
- */
-public class AsciiReportRenderer extends TextProjectReportRenderer implements DependencyReportRenderer {
-    private boolean hasConfigs;
-
-    public AsciiReportRenderer() {
-    }
-
-    public AsciiReportRenderer(Appendable writer) {
-        super(writer);
-    }
-
-    @Override
-    public void startProject(Project project) {
-        super.startProject(project);
-        hasConfigs = false;
-    }
-
-    @Override
-    public void completeProject(Project project) {
-        if (!hasConfigs) {
-            getFormatter().format("No configurations%n");
-        }
-        super.completeProject(project);
-    }
-
-    public void startConfiguration(Configuration configuration) {
-        hasConfigs = true;
-        getFormatter().format("%s%s%n", configuration.getName(), getDescription(configuration));
-    }
-
-    private String getDescription(Configuration configuration) {
-        return GUtil.isTrue(configuration.getDescription()) ? " - " + configuration.getDescription() : "";
-    }
-
-    public void completeConfiguration(Configuration configuration) {
-    }
-
-    public void render(ResolvedConfiguration resolvedConfiguration) throws IOException
-    {
-        Set<ResolvedDependency> mergedRoots = mergeChildren(resolvedConfiguration.getFirstLevelModuleDependencies());
-        for (ResolvedDependency root : mergedRoots) {
-            render(root, 1);
-        }
-    }
-
-    private void render(ResolvedDependency resolvedDependency, int depth) throws IOException
-    {
-        getFormatter().format(getIndent(depth));
-		getFormatter().format("%s:%s%n", resolvedDependency.getName(),
-                resolvedDependency.getConfiguration());
-
-        Collection<ResolvedDependency> mergedChildren = mergeChildren(resolvedDependency.getChildren());
-
-		for(ResolvedDependency childResolvedDependency : mergedChildren)
-		{
-			render(childResolvedDependency, depth + 1);
-		}
-    }
-
-    private Set<ResolvedDependency> mergeChildren(Set<ResolvedDependency> children) {
-        Map<String, Set<ResolvedDependency>> mergedGroups = new HashMap<String, Set<ResolvedDependency>>();
-        for (ResolvedDependency child : children) {
-            Set<ResolvedDependency> mergeGroup = mergedGroups.get(child.getName());
-            if (mergeGroup == null) {
-                mergedGroups.put(child.getName(), mergeGroup = new HashSet<ResolvedDependency>());
-            }
-            mergeGroup.add(child);
-        }
-        Set<ResolvedDependency> mergedChildren = new HashSet<ResolvedDependency>();
-        for (Set<ResolvedDependency> mergedGroup : mergedGroups.values()) {
-            mergedChildren.add(new MergedResolvedDependency(mergedGroup));
-        }
-        return mergedChildren;
-    }
-
-    private String getIndent(int depth)
-	{
-		StringBuilder buffer = new StringBuilder();
-
-		for(int x = 0; x < depth - 1; x++)
-		{
-            if(x > 0)
-            {
-                buffer.append("|");
-            }
-
-			buffer.append("      ");
-		}
-
-		buffer.append("|-----");
-
-		return buffer.toString();
-	}
-
-    private static class MergedResolvedDependency implements ResolvedDependency {
-        private Set<ResolvedDependency> mergedResolvedDependencies = new LinkedHashSet<ResolvedDependency>();
-
-        public MergedResolvedDependency(Set<ResolvedDependency> mergedResolvedDependencies) {
-            assert !mergedResolvedDependencies.isEmpty();
-            this.mergedResolvedDependencies = mergedResolvedDependencies;
-        }
-
-        public String getName() {
-            return mergedResolvedDependencies.iterator().next().getName();
-        }
-
-        public String getModuleName() {
-            return mergedResolvedDependencies.iterator().next().getModuleName();
-        }
-
-        public String getModuleGroup() {
-            return mergedResolvedDependencies.iterator().next().getModuleGroup();
-        }
-
-        public String getModuleVersion() {
-            return mergedResolvedDependencies.iterator().next().getModuleVersion();
-        }
-
-        public String getConfiguration() {
-            String mergedConfiguration = "";
-            for (ResolvedDependency mergedResolvedDependency : mergedResolvedDependencies) {
-                mergedConfiguration += mergedResolvedDependency.getConfiguration() + ",";
-            }
-            return mergedConfiguration.substring(0, mergedConfiguration.length() - 1);
-        }
-
-        public Set<ResolvedDependency> getChildren() {
-            Set<ResolvedDependency> mergedChildren = new LinkedHashSet<ResolvedDependency>();
-            for (ResolvedDependency mergedResolvedDependency : mergedResolvedDependencies) {
-                mergedChildren.addAll(mergedResolvedDependency.getChildren());
-            }
-            return mergedChildren;
-        }
-
-        public Set<ResolvedDependency> getParents() {
-            throw new UnsupportedOperationException();
-        }
-
-        public Set<ResolvedArtifact> getModuleArtifacts() {
-            Set<ResolvedArtifact> mergedModuleArtifacts = new LinkedHashSet<ResolvedArtifact>();
-            for (ResolvedDependency mergedResolvedDependency : mergedResolvedDependencies) {
-                mergedModuleArtifacts.addAll(mergedResolvedDependency.getModuleArtifacts());
-            }
-            return mergedModuleArtifacts;
-        }
-
-        public Set<ResolvedArtifact> getAllModuleArtifacts() {
-            throw new UnsupportedOperationException();
-        }
-
-        public Set<ResolvedArtifact> getParentArtifacts(ResolvedDependency parent) {
-            throw new UnsupportedOperationException();
-        }
-
-        public Set<ResolvedArtifact> getArtifacts(ResolvedDependency parent) {
-            throw new UnsupportedOperationException();
-        }
-
-        public Set<ResolvedArtifact> getAllArtifacts(ResolvedDependency parent) {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportRenderer.java
deleted file mode 100644
index 89c9b3b..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportRenderer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ResolvedConfiguration;
-
-import java.io.IOException;
-
-/**
- * A {@code DependencyReportRenderer} is responsible for rendering the model of a project dependency report.
- *
- * @author Phil Messenger
- */
-public interface DependencyReportRenderer extends ProjectReportRenderer {
-    /**
-     * Starts rendering the given configuration.
-     * @param configuration The configuration.
-     */
-    void startConfiguration(Configuration configuration);
-
-    /**
-     * Writes the given dependency graph for the current configuration.
-     *
-     * @param resolvedConfiguration The resolved configuration.
-     */
-    void render(ResolvedConfiguration resolvedConfiguration) throws IOException;
-
-    /**
-     * Completes the rendering of the given configuration.
-     * @param configuration The configuration
-     */
-    void completeConfiguration(Configuration configuration);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTask.java
index 0e16f1a..cd6178c 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTask.java
@@ -17,6 +17,9 @@ package org.gradle.api.tasks.diagnostics;
 
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.tasks.diagnostics.internal.AsciiReportRenderer;
+import org.gradle.api.tasks.diagnostics.internal.DependencyReportRenderer;
+import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
 
 import java.io.IOException;
 import java.util.Comparator;
@@ -25,7 +28,7 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 
 /**
- * The {@code DependencyReportTask} displays the dependency tree for a project. Can be configured to output to a file,
+ * Displays the dependency tree for a project. Can be configured to output to a file,
  * and to optionally output a graphviz compatible "dot" graph. This task is used when you execute the dependency list
  * command-line option.
  *
@@ -37,7 +40,7 @@ public class DependencyReportTask extends AbstractReportTask {
 
     private Set<Configuration> configurations;
 
-    public ProjectReportRenderer getRenderer() {
+    public ReportRenderer getRenderer() {
         return renderer;
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/GraphvizReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/GraphvizReportRenderer.java
deleted file mode 100644
index 905d556..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/GraphvizReportRenderer.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ResolvedConfiguration;
-import org.gradle.api.artifacts.ResolvedDependency;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * DependencyGrAaphRenderer that emits simple graphviz dot notation for a dependency tree.
- *
- * @author Phil Messenger
- */
-public class GraphvizReportRenderer extends TextProjectReportRenderer implements DependencyReportRenderer {
-    @Override
-    public void startProject(Project project) {
-        // Do nothing
-    }
-
-    public void startConfiguration(Configuration configuration) {
-        // Do nothing
-    }
-
-    public void completeConfiguration(Configuration configuration) {
-        // Do nothing
-    }
-
-    public void render(ResolvedConfiguration resolvedConfiguration) throws IOException {
-        getFormatter().format("digraph %s{%n", "SomeConf");
-
-        Set<String> edges = new HashSet<String>();
-
-        for (ResolvedDependency resolvedDependency : resolvedConfiguration.getFirstLevelModuleDependencies()) {
-            buildDotDependencyTree(resolvedDependency, edges);
-        }
-
-        for (String edge : edges) {
-            getFormatter().format("%s%n", edge);
-        }
-
-        getFormatter().format("}%n");
-    }
-
-    private void buildDotDependencyTree(ResolvedDependency root, Set<String> edges) {
-        if (root.getAllModuleArtifacts().isEmpty()) {
-            return;
-        }
-        for (ResolvedDependency dep : root.getChildren()) {
-            String edge = "\"" + root.toString() + "\" -> \"" + dep.toString().replace('-', '_') + "\";";
-            edges.add(edge);
-        }
-
-        for (ResolvedDependency dep : root.getChildren()) {
-            buildDotDependencyTree(dep, edges);
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportRenderer.java
deleted file mode 100644
index 1b92eb7..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportRenderer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.gradle.api.Project;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * <p>A {@code ProjectReportRenderer} is responsible for rendering the model of a project report.</p>
- */
-public interface ProjectReportRenderer {
-    /**
-     * Sets the output file for the report. This method must be called before any other methods on this renderer.
-     *
-     * @param file The output file, never null.
-     */
-    void setOutputFile(File file) throws IOException;
-
-    /**
-     * Starts visiting a project.
-     *
-     * @param project The project, never null.
-     */
-    void startProject(Project project);
-
-    /**
-     * Completes visiting a project.
-     *
-     * @param project The project, never null.
-     */
-    void completeProject(Project project);
-
-    /**
-     * Completes this report. This method must be called last on this renderer.
-     */
-    void complete() throws IOException;
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java
new file mode 100644
index 0000000..baa38f4
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTask.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.diagnostics.internal.GraphRenderer;
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.configuration.ImplicitTasksConfigurer;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.util.GUtil;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.gradle.logging.StyledTextOutput.Style.*;
+import static org.gradle.logging.StyledTextOutput.Style.UserInput;
+
+/**
+ * <p>Displays a list of projects in the build. It is used when you use the project list command-line option.</p>
+ */
+public class ProjectReportTask extends DefaultTask {
+    private StyledTextOutput textOutput = getServices().get(StyledTextOutputFactory.class).create(ProjectReportTask.class);
+
+    @TaskAction
+    void listProjects() {
+        GradleLauncherMetaData metaData = new GradleLauncherMetaData();
+        Project project = getProject();
+
+        textOutput.println();
+        render(project, new GraphRenderer(textOutput), true);
+        if (project.getChildProjects().isEmpty()) {
+            textOutput.withStyle(Info).text("No sub-projects");
+            textOutput.println();
+        }
+
+        textOutput.println();
+        textOutput.text("To see a list of the tasks of a project, run ");
+        metaData.describeCommand(textOutput.withStyle(UserInput), String.format("<project-path>:%s", ImplicitTasksConfigurer.TASKS_TASK));
+        textOutput.println();
+
+        textOutput.text("For example, try running ");
+        Project exampleProject = project.getChildProjects().isEmpty() ? project : getChildren(project).get(0);
+        metaData.describeCommand(textOutput.withStyle(UserInput), exampleProject.absoluteProjectPath(ImplicitTasksConfigurer.TASKS_TASK));
+        textOutput.println();
+
+        if (project != project.getRootProject()) {
+            textOutput.println();
+            textOutput.text("To see a list of all the projects in this build, run ");
+            metaData.describeCommand(textOutput.withStyle(UserInput), project.getRootProject().absoluteProjectPath(ImplicitTasksConfigurer.PROJECTS_TASK));
+            textOutput.println();
+        }
+    }
+
+    private void render(final Project project, GraphRenderer renderer, boolean lastChild) {
+        renderer.visit(new Action<StyledTextOutput>() {
+            public void execute(StyledTextOutput styledTextOutput) {
+                styledTextOutput.text(StringUtils.capitalize(project.toString()));
+                if (GUtil.isTrue(project.getDescription())) {
+                    getTextOutput().withStyle(Description).format(" - %s", project.getDescription());
+                }
+            }
+        }, lastChild);
+        renderer.startChildren();
+        List<Project> children = getChildren(project);
+        for (Project child : children) {
+            render(child, renderer, child == children.get(children.size() - 1));
+        }
+        renderer.completeChildren();
+    }
+
+    private List<Project> getChildren(Project project) {
+        List<Project> children = new ArrayList<Project>(project.getChildProjects().values());
+        Collections.sort(children);
+        return children;
+    }
+
+    public StyledTextOutput getTextOutput() {
+        return textOutput;
+    }
+
+    public void setTextOutput(StyledTextOutput textOutput) {
+        this.textOutput = textOutput;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportRenderer.java
deleted file mode 100644
index e6c93f4..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportRenderer.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-/**
- * <p>A {@code PropertyReportRenderer} is responsible for rendering the model of a property report.</p>
- */
-public class PropertyReportRenderer extends TextProjectReportRenderer {
-
-    public PropertyReportRenderer() {
-    }
-
-    public PropertyReportRenderer(Appendable out) {
-        super(out);
-    }
-
-    /**
-     * Writes a property for the current project.
-     *
-     * @param name The name of the property
-     * @param value The value of the property
-     */
-    public void addProperty(String name, Object value) {
-        getFormatter().format("%s: %s%n", name, value);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTask.java
index 44372df..b86ce4c 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTask.java
@@ -16,19 +16,20 @@
 package org.gradle.api.tasks.diagnostics;
 
 import org.gradle.api.Project;
+import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
+import org.gradle.api.tasks.diagnostics.internal.PropertyReportRenderer;
 
 import java.io.IOException;
 import java.util.Map;
 import java.util.TreeMap;
 
 /**
- * The {@code PropertyListTask} prints out the properties of a project, and its sub-projects and tasks. This task is
- * used when you execute the property list command-line option.
+ * Displays the properties of a project. This task is used when you execute the property list command-line option.
  */
 public class PropertyReportTask extends AbstractReportTask {
     private PropertyReportRenderer renderer = new PropertyReportRenderer();
 
-    public ProjectReportRenderer getRenderer() {
+    public ReportRenderer getRenderer() {
         return renderer;
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskDetails.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskDetails.java
deleted file mode 100644
index 78ffeba..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskDetails.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import java.util.Set;
-
-public interface TaskDetails extends Comparable<TaskDetails> {
-    String getPath();
-
-    String getDescription();
-
-    Set<String> getDependencies();
-
-    Set<TaskDetails> getChildren();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportModel.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportModel.java
deleted file mode 100644
index fb5b3cd..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportModel.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import com.google.common.collect.Ordering;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.TreeMultimap;
-import org.gradle.api.Task;
-import org.gradle.api.internal.DirectedGraph;
-import org.gradle.api.internal.GraphAggregator;
-import org.gradle.util.GUtil;
-
-import java.util.*;
-
-public class TaskReportModel {
-    private final SetMultimap<String, TaskDetails> groups = TreeMultimap.create(GUtil.emptyLast(GUtil.caseInsensitive()), Ordering.natural());
-
-    public void calculate(final Collection<? extends Task> tasks) {
-        Set<Task> topLevelTasks = new LinkedHashSet<Task>();
-        for (final Task task : tasks) {
-            if (GUtil.isTrue(task.getGroup())) {
-                topLevelTasks.add(task);
-            }
-        }
-        GraphAggregator<Task> aggregator = new GraphAggregator<Task>(new DirectedGraph<Task, Object>() {
-            public void getNodeValues(Task node, Collection<Object> values, Collection<Task> connectedNodes) {
-                for (Task dep : node.getTaskDependencies().getDependencies(node)) {
-                    if (tasks.contains(dep)) {
-                        connectedNodes.add(dep);
-                    }
-                }
-            }
-        });
-
-        GraphAggregator.Result<Task> result = aggregator.group(topLevelTasks, tasks);
-        for (Task task : result.getTopLevelNodes()) {
-            Set<Task> nodesForThisTask = new TreeSet<Task>(result.getNodes(task));
-            Set<TaskDetails> children = new LinkedHashSet<TaskDetails>();
-            Set<String> dependencies = new TreeSet<String>();
-            for (Task node : nodesForThisTask) {
-                if (node != task) {
-                    children.add(new TaskDetailsImpl(node, Collections.<TaskDetails>emptySet(),
-                            Collections.<String>emptySet()));
-                }
-                for (Task dep : node.getTaskDependencies().getDependencies(node)) {
-                    if (topLevelTasks.contains(dep) || !tasks.contains(dep)) {
-                        dependencies.add(dep.getPath());
-                    }
-                }
-            }
-
-            String group = topLevelTasks.contains(task) ? task.getGroup() : "";
-            groups.put(group, new TaskDetailsImpl(task, children, dependencies));
-        }
-    }
-
-    public Set<String> getGroups() {
-        return groups.keySet();
-    }
-
-    public Set<TaskDetails> getTasksForGroup(String group) {
-        if (!groups.containsKey(group)) {
-            throw new IllegalArgumentException(String.format("Unknown group '%s'", group));
-        }
-        return groups.get(group);
-    }
-
-    private static class TaskDetailsImpl implements TaskDetails {
-        private final Task task;
-        private final Set<TaskDetails> children;
-        private final Set<String> dependencies;
-
-        public TaskDetailsImpl(Task task, Set<TaskDetails> children, Set<String> dependencies) {
-            this.task = task;
-            this.children = children;
-            this.dependencies = dependencies;
-        }
-
-        public String getDescription() {
-            return task.getDescription();
-        }
-
-        public String getPath() {
-            return task.getPath();
-        }
-
-        @Override
-        public String toString() {
-            return task.toString();
-        }
-
-        public Task getTask() {
-            return task;
-        }
-
-        public Set<String> getDependencies() {
-            return dependencies;
-        }
-
-        public Set<TaskDetails> getChildren() {
-            return children;
-        }
-
-        public int compareTo(TaskDetails o) {
-            return getPath().compareTo(o.getPath());
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportRenderer.java
deleted file mode 100644
index 73f2d99..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportRenderer.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.diagnostics;
-
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.Project;
-import org.gradle.api.Rule;
-import org.gradle.util.GUtil;
-
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-/**
- * <p>A {@code TaskReportRenderer} is responsible for rendering the model of a project task report.</p>
- *
- * @author Hans Dockter
- */
-public class TaskReportRenderer extends TextProjectReportRenderer {
-    private boolean currentProjectHasTasks;
-    private boolean currentProjectHasRules;
-    private boolean hasContent;
-    private boolean detail;
-
-    public TaskReportRenderer() {
-    }
-
-    public TaskReportRenderer(Appendable writer) {
-        super(writer);
-    }
-
-    @Override
-    public void startProject(Project project) {
-        currentProjectHasTasks = false;
-        currentProjectHasRules = false;
-        hasContent = true;
-        detail = false;
-        super.startProject(project);
-    }
-
-    public void showDetail(boolean detail) {
-        this.detail = detail;
-    }
-    
-    /**
-     * Writes the default task names for the current project.
-     *
-     * @param defaultTaskNames The default task names (must not be null)
-     */
-    public void addDefaultTasks(List<String> defaultTaskNames) {
-        if (defaultTaskNames.size() > 0) {
-            getFormatter().format("Default tasks: %s%n", GUtil.join(defaultTaskNames, ", "));
-            hasContent = true;
-        }
-    }
-
-    public void startTaskGroup(String taskGroup) {
-        if (!GUtil.isTrue(taskGroup)) {
-            addHeader(currentProjectHasTasks ? "Other tasks" : "Tasks");
-        } else {
-            addHeader(StringUtils.capitalize(taskGroup) + " tasks");
-        }
-        currentProjectHasTasks = true;
-    }
-
-    /**
-     * Writes a task for the current project.
-     *
-     * @param task The task
-     */
-    public void addTask(TaskDetails task) {
-        writeTask(task, "");
-    }
-
-    public void addChildTask(TaskDetails task) {
-        if (detail) {
-            writeTask(task, "    ");
-        }
-    }
-
-    private void writeTask(TaskDetails task, String prefix) {
-        getFormatter().format("%s%s%s", prefix, task.getPath(), getDescription(task));
-        if (detail) {
-            SortedSet<String> sortedDependencies = new TreeSet<String>();
-            for (String dependency : task.getDependencies()) {
-                sortedDependencies.add(dependency);
-            }
-            if (sortedDependencies.size() > 0) {
-                getFormatter().format(" [%s]", GUtil.join(sortedDependencies, ", "));
-            }
-        }
-        getFormatter().format("%n");
-    }
-
-    private void addHeader(String header) {
-        if (hasContent) {
-            getFormatter().format("%n");
-        }
-        hasContent = true;
-        getFormatter().format("%s%n", header);
-        for (int i = 0; i < header.length(); i++) {
-            getFormatter().format("-");
-        }
-        getFormatter().format("%n");
-    }
-
-    private String getDescription(TaskDetails task) {
-        return GUtil.isTrue(task.getDescription()) ? " - " + task.getDescription() : "";
-    }
-
-    /**
-     * Marks the end of the tasks for the current project.
-     */
-    public void completeTasks() {
-        if (!currentProjectHasTasks) {
-            getFormatter().format("No tasks%n");
-            hasContent = true;
-        }
-    }
-
-    /**
-     * Writes a rule for the current project.
-     *
-     * @param rule The rule
-     */
-    public void addRule(Rule rule) {
-        if (!currentProjectHasRules) {
-            addHeader("Rules");
-        }
-        getFormatter().format("%s%n", GUtil.elvis(rule.getDescription(), ""));
-        currentProjectHasRules = true;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java
index 3633ae7..aa7f8ed 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TaskReportTask.java
@@ -17,18 +17,22 @@ package org.gradle.api.tasks.diagnostics;
 
 import org.gradle.api.Project;
 import org.gradle.api.Rule;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.CommandLineOption;
+import org.gradle.api.tasks.diagnostics.internal.*;
+import org.gradle.util.GUtil;
 
 import java.io.IOException;
 
 /**
- * <p>The {@code TaskReportTask} prints out the list of tasks in the project, and its subprojects. It is used when you
- *  use the task list command-line option.</p>
+ * <p>Displays a list of tasks in the project. It is used when you use the task list command-line option.</p>
  */
 public class TaskReportTask extends AbstractReportTask {
     private TaskReportRenderer renderer = new TaskReportRenderer();
+
     private boolean detail;
 
-    public ProjectReportRenderer getRenderer() {
+    public ReportRenderer getRenderer() {
         return renderer;
     }
 
@@ -36,6 +40,7 @@ public class TaskReportTask extends AbstractReportTask {
         this.renderer = renderer;
     }
 
+    @CommandLineOption(options = "all", description = "Show additional tasks and detail.")
     public void setShowDetail(boolean detail) {
         this.detail = detail;
     }
@@ -48,8 +53,24 @@ public class TaskReportTask extends AbstractReportTask {
         renderer.showDetail(isDetail());
         renderer.addDefaultTasks(project.getDefaultTasks());
 
-        TaskReportModel model = new TaskReportModel();
-        model.calculate(project.getTasks().getAll());
+        AggregateMultiProjectTaskReportModel aggregateModel = new AggregateMultiProjectTaskReportModel(!isDetail());
+        TaskDetailsFactory taskDetailsFactory = new TaskDetailsFactory(project);
+
+        SingleProjectTaskReportModel projectTaskModel = new SingleProjectTaskReportModel(taskDetailsFactory);
+        ProjectInternal projectInternal = (ProjectInternal) project;
+        projectTaskModel.build(GUtil.addSets(projectInternal.getTasks(), projectInternal.getImplicitTasks()));
+        aggregateModel.add(projectTaskModel);
+
+        for (Project subprojects : project.getSubprojects()) {
+            SingleProjectTaskReportModel subprojectTaskModel = new SingleProjectTaskReportModel(taskDetailsFactory);
+            subprojectTaskModel.build(subprojects.getTasks().getAll());
+            aggregateModel.add(subprojectTaskModel);
+        }
+
+        aggregateModel.build();
+
+        DefaultGroupTaskReportModel model = new DefaultGroupTaskReportModel();
+        model.build(aggregateModel);
 
         for (String group : model.getGroups()) {
             renderer.startTaskGroup(group);
@@ -60,8 +81,8 @@ public class TaskReportTask extends AbstractReportTask {
                 }
             }
         }
-
         renderer.completeTasks();
+
         for (Rule rule : project.getTasks().getRules()) {
             renderer.addRule(rule);
         }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TextProjectReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TextProjectReportRenderer.java
deleted file mode 100644
index 2bad8bb..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/TextProjectReportRenderer.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.gradle.api.Project;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Formatter;
-
-/**
- * <p>A basic {@link ProjectReportRenderer} which writes out a text report.
- */
-public class TextProjectReportRenderer implements ProjectReportRenderer {
-    public static final String SEPARATOR = "------------------------------------------------------------";
-    private Appendable writer;
-    private boolean close;
-    private Formatter formatter;
-
-    public TextProjectReportRenderer() {
-        this(System.out);
-    }
-
-    public TextProjectReportRenderer(Appendable writer) {
-        setWriter(writer, false);
-    }
-
-    public void setOutputFile(File file) throws IOException {
-        cleanupWriter();
-        setWriter(new FileWriter(file), true);
-    }
-
-    public void startProject(Project project) {
-        formatter.format("%n%s%n", SEPARATOR);
-        if (project.getRootProject() == project) {
-            formatter.format("Root Project%n");
-        } else {
-            formatter.format("Project %s%n", project.getPath());
-        }
-        formatter.format("%s%n", SEPARATOR);
-    }
-
-    public void completeProject(Project project) {
-    }
-
-    public void complete() throws IOException {
-        cleanupWriter();
-        setWriter(System.out, false);
-    }
-
-    private void setWriter(Appendable writer, boolean close) {
-        this.writer = writer;
-        this.close = close;
-        formatter = new Formatter(writer);
-    }
-
-    private void cleanupWriter() throws IOException {
-        formatter.flush();
-        if (close) {
-            ((Closeable) writer).close();
-        }
-    }
-
-    protected Appendable getWriter() {
-        return writer;
-    }
-
-    protected Formatter getFormatter() {
-        return formatter;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/AggregateMultiProjectTaskReportModel.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/AggregateMultiProjectTaskReportModel.java
new file mode 100644
index 0000000..4e41cb1
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/AggregateMultiProjectTaskReportModel.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.TreeMultimap;
+import org.gradle.util.Path;
+
+import java.util.*;
+
+public class AggregateMultiProjectTaskReportModel implements TaskReportModel {
+    private List<TaskReportModel> projects = new ArrayList<TaskReportModel>();
+    private SetMultimap<String, TaskDetails> groups;
+    private final boolean mergeTasksWithSameName;
+
+    public AggregateMultiProjectTaskReportModel(boolean mergeTasksWithSameName) {
+        this.mergeTasksWithSameName = mergeTasksWithSameName;
+    }
+
+    public void add(TaskReportModel project) {
+        projects.add(project);
+    }
+
+    public void build() {
+        groups = TreeMultimap.create(new Comparator<String>() {
+            public int compare(String string1, String string2) {
+                return string1.compareToIgnoreCase(string2);
+            }
+        }, new Comparator<TaskDetails>() {
+            public int compare(TaskDetails task1, TaskDetails task2) {
+                return task1.getPath().compareTo(task2.getPath());
+            }
+        });
+        for (TaskReportModel project : projects) {
+            for (String group : project.getGroups()) {
+                for (final TaskDetails task : project.getTasksForGroup(group)) {
+                    groups.put(group, mergeTasksWithSameName ? new MergedTaskDetails(task) : task);
+                }
+            }
+        }
+    }
+
+    public Set<String> getGroups() {
+        return groups.keySet();
+    }
+
+    public Set<TaskDetails> getTasksForGroup(String group) {
+        return groups.get(group);
+    }
+
+    private static class MergedTaskDetails implements TaskDetails {
+        private final TaskDetails task;
+
+        public MergedTaskDetails(TaskDetails task) {
+            this.task = task;
+        }
+
+        public Path getPath() {
+            return Path.path(task.getPath().getName());
+        }
+
+        public Set<TaskDetails> getChildren() {
+            return task.getChildren();
+        }
+
+        public String getDescription() {
+            return task.getDescription();
+        }
+
+        public Set<TaskDetails> getDependencies() {
+            return task.getDependencies();
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/AsciiReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/AsciiReportRenderer.java
new file mode 100644
index 0000000..d2e29b7
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/AsciiReportRenderer.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ResolvedConfiguration;
+import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.util.GUtil;
+
+import java.io.IOException;
+import java.util.*;
+
+import static org.gradle.logging.StyledTextOutput.Style.*;
+
+/**
+ * Simple dependency graph renderer that emits an ASCII tree.
+ *
+ * @author Phil Messenger
+ */
+public class AsciiReportRenderer extends TextReportRenderer implements DependencyReportRenderer {
+    private boolean hasConfigs;
+    private GraphRenderer renderer;
+
+    @Override
+    public void startProject(Project project) {
+        super.startProject(project);
+        hasConfigs = false;
+    }
+
+    @Override
+    public void completeProject(Project project) {
+        if (!hasConfigs) {
+            getTextOutput().withStyle(Info).println("No configurations");
+        }
+        super.completeProject(project);
+    }
+
+    public void startConfiguration(final Configuration configuration) {
+        if (hasConfigs) {
+            getTextOutput().println();
+        }
+        hasConfigs = true;
+        renderer = new GraphRenderer(getTextOutput());
+        renderer.visit(new Action<StyledTextOutput>() {
+            public void execute(StyledTextOutput styledTextOutput) {
+                getTextOutput().withStyle(Identifier).text(configuration.getName());
+                getTextOutput().withStyle(Description).text(getDescription(configuration));
+            }
+        }, true);
+    }
+
+    private String getDescription(Configuration configuration) {
+        return GUtil.isTrue(configuration.getDescription()) ? " - " + configuration.getDescription() : "";
+    }
+
+    public void completeConfiguration(Configuration configuration) {
+    }
+
+    public void render(ResolvedConfiguration resolvedConfiguration) throws IOException {
+        Set<MergedResolvedDependency> mergedRoots = mergeChildren(resolvedConfiguration.getFirstLevelModuleDependencies());
+        if (mergedRoots.isEmpty()) {
+            getTextOutput().withStyle(Info).text("No dependencies");
+            getTextOutput().println();
+            return;
+        }
+        renderChildren(mergedRoots);
+    }
+
+    private void render(final MergedResolvedDependency resolvedDependency, boolean lastChild) {
+        renderer.visit(new Action<StyledTextOutput>() {
+            public void execute(StyledTextOutput styledTextOutput) {
+                getTextOutput().text(resolvedDependency.getName());
+                getTextOutput().withStyle(Info).format(" [%s]", resolvedDependency.getConfiguration());
+            }
+        }, lastChild);
+        renderChildren(mergeChildren(resolvedDependency.getChildren()));
+    }
+
+    private void renderChildren(Set<MergedResolvedDependency> children) {
+        renderer.startChildren();
+        List<MergedResolvedDependency> mergedChildren = new ArrayList<MergedResolvedDependency>(children);
+        for (int i = 0; i < mergedChildren.size(); i++) {
+            MergedResolvedDependency dependency = mergedChildren.get(i);
+            render(dependency, i == mergedChildren.size() - 1);
+        }
+        renderer.completeChildren();
+    }
+
+    private Set<MergedResolvedDependency> mergeChildren(Set<ResolvedDependency> children) {
+        Map<String, Set<ResolvedDependency>> mergedGroups = new LinkedHashMap<String, Set<ResolvedDependency>>();
+        for (ResolvedDependency child : children) {
+            Set<ResolvedDependency> mergeGroup = mergedGroups.get(child.getName());
+            if (mergeGroup == null) {
+                mergedGroups.put(child.getName(), mergeGroup = new LinkedHashSet<ResolvedDependency>());
+            }
+            mergeGroup.add(child);
+        }
+        Set<MergedResolvedDependency> mergedChildren = new LinkedHashSet<MergedResolvedDependency>();
+        for (Set<ResolvedDependency> mergedGroup : mergedGroups.values()) {
+            mergedChildren.add(new MergedResolvedDependency(mergedGroup));
+        }
+        return mergedChildren;
+    }
+
+    private static class MergedResolvedDependency {
+        private Set<ResolvedDependency> mergedResolvedDependencies = new LinkedHashSet<ResolvedDependency>();
+
+        public MergedResolvedDependency(Set<ResolvedDependency> mergedResolvedDependencies) {
+            assert !mergedResolvedDependencies.isEmpty();
+            this.mergedResolvedDependencies = mergedResolvedDependencies;
+        }
+
+        public String getName() {
+            return mergedResolvedDependencies.iterator().next().getName();
+        }
+
+        public String getConfiguration() {
+            String mergedConfiguration = "";
+            for (ResolvedDependency mergedResolvedDependency : mergedResolvedDependencies) {
+                mergedConfiguration += mergedResolvedDependency.getConfiguration() + ",";
+            }
+            return mergedConfiguration.substring(0, mergedConfiguration.length() - 1);
+        }
+
+        public Set<ResolvedDependency> getChildren() {
+            Set<ResolvedDependency> mergedChildren = new LinkedHashSet<ResolvedDependency>();
+            for (ResolvedDependency mergedResolvedDependency : mergedResolvedDependencies) {
+                mergedChildren.addAll(mergedResolvedDependency.getChildren());
+            }
+            return mergedChildren;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/DefaultGroupTaskReportModel.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/DefaultGroupTaskReportModel.java
new file mode 100644
index 0000000..ca009c2
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/DefaultGroupTaskReportModel.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.TreeMultimap;
+import org.gradle.util.GUtil;
+import org.gradle.util.Path;
+
+import java.util.Comparator;
+import java.util.Set;
+
+public class DefaultGroupTaskReportModel implements TaskReportModel {
+    public static final String OTHER_GROUP = "other";
+    private static final Comparator<String> STRING_COMPARATOR = GUtil.caseInsensitive();
+    private SetMultimap<String, TaskDetails> groups;
+
+    public void build(TaskReportModel model) {
+        Comparator<String> keyComparator = GUtil.last(GUtil.last(STRING_COMPARATOR, OTHER_GROUP), TaskReportModel.DEFAULT_GROUP);
+        Comparator<TaskDetails> taskComparator = new Comparator<TaskDetails>() {
+            public int compare(TaskDetails task1, TaskDetails task2) {
+                int diff = STRING_COMPARATOR.compare(task1.getPath().getName(), task2.getPath().getName());
+                if (diff != 0) {
+                    return diff;
+                }
+                Path parent1 = task1.getPath().getParent();
+                Path parent2 = task2.getPath().getParent();
+                if (parent1 == null && parent2 != null) {
+                    return -1;
+                }
+                if (parent1 != null && parent2 == null) {
+                    return 1;
+                }
+                if (parent1 == null) {
+                    return 0;
+                }
+                return parent1.compareTo(parent2);
+            }
+        };
+        groups = TreeMultimap.create(keyComparator, taskComparator);
+        for (String group : model.getGroups()) {
+            groups.putAll(group, model.getTasksForGroup(group));
+        }
+        String otherGroupName = findOtherGroup(groups.keySet());
+        if (otherGroupName != null && groups.keySet().contains(TaskReportModel.DEFAULT_GROUP)) {
+            groups.putAll(otherGroupName, groups.removeAll(TaskReportModel.DEFAULT_GROUP));
+        }
+        if (groups.keySet().contains(TaskReportModel.DEFAULT_GROUP) && groups.keySet().size() > 1) {
+            groups.putAll(OTHER_GROUP, groups.removeAll(TaskReportModel.DEFAULT_GROUP));
+        }
+    }
+
+    private String findOtherGroup(Set<String> groupNames) {
+        for (String groupName : groupNames) {
+            if (groupName.equalsIgnoreCase(OTHER_GROUP)) {
+                return groupName;
+            }
+        }
+        return null;
+    }
+
+    public Set<String> getGroups() {
+        return groups.keySet();
+    }
+
+    public Set<TaskDetails> getTasksForGroup(String group) {
+        return groups.get(group);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/DependencyReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/DependencyReportRenderer.java
new file mode 100644
index 0000000..f3b56c3
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/DependencyReportRenderer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ResolvedConfiguration;
+
+import java.io.IOException;
+
+/**
+ * A {@code DependencyReportRenderer} is responsible for rendering the model of a project dependency report.
+ *
+ * @author Phil Messenger
+ */
+public interface DependencyReportRenderer extends ReportRenderer {
+    /**
+     * Starts rendering the given configuration.
+     * @param configuration The configuration.
+     */
+    void startConfiguration(Configuration configuration);
+
+    /**
+     * Writes the given dependency graph for the current configuration.
+     *
+     * @param resolvedConfiguration The resolved configuration.
+     */
+    void render(ResolvedConfiguration resolvedConfiguration) throws IOException;
+
+    /**
+     * Completes the rendering of the given configuration.
+     * @param configuration The configuration
+     */
+    void completeConfiguration(Configuration configuration);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/GraphRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/GraphRenderer.java
new file mode 100644
index 0000000..406c999
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/GraphRenderer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Action;
+import org.gradle.logging.StyledTextOutput;
+
+import static org.gradle.logging.StyledTextOutput.Style.Info;
+
+public class GraphRenderer {
+    private final StyledTextOutput output;
+    private StringBuilder prefix = new StringBuilder();
+    private boolean seenRootChildren;
+    private boolean lastChild = true;
+
+    public GraphRenderer(StyledTextOutput output) {
+        this.output = output;
+    }
+
+    /**
+     * Visits a node in the graph.
+     */
+    public void visit(Action<? super StyledTextOutput> node, boolean lastChild) {
+        if (seenRootChildren) {
+            output.withStyle(Info).text(prefix + (lastChild ? "\\--- " : "+--- "));
+        }
+        this.lastChild = lastChild;
+        node.execute(output);
+        output.println();
+    }
+
+    /**
+     * Starts visiting the children of the most recently visited node.
+     */
+    public void startChildren() {
+        if (seenRootChildren) {
+            prefix.append(lastChild ? "     " : "|    ");
+        }
+        seenRootChildren = true;
+    }
+
+    /**
+     * Completes visiting the children of the node which most recently started visiting children.
+     */
+    public void completeChildren() {
+        if (prefix.length() == 0) {
+            seenRootChildren = false;
+        } else {
+            prefix.setLength(prefix.length() - 5);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/GraphvizReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/GraphvizReportRenderer.java
new file mode 100644
index 0000000..9653fb8
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/GraphvizReportRenderer.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ResolvedConfiguration;
+import org.gradle.api.artifacts.ResolvedDependency;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * DependencyGraphRenderer that emits simple graphviz dot notation for a dependency tree.
+ *
+ * @author Phil Messenger
+ */
+public class GraphvizReportRenderer extends TextReportRenderer implements DependencyReportRenderer {
+    @Override
+    public void startProject(Project project) {
+        // Do nothing
+    }
+
+    public void startConfiguration(Configuration configuration) {
+        // Do nothing
+    }
+
+    public void completeConfiguration(Configuration configuration) {
+        // Do nothing
+    }
+
+    public void render(ResolvedConfiguration resolvedConfiguration) throws IOException {
+        getTextOutput().println("digraph SomeConf{");
+
+        Set<String> edges = new HashSet<String>();
+
+        for (ResolvedDependency resolvedDependency : resolvedConfiguration.getFirstLevelModuleDependencies()) {
+            buildDotDependencyTree(resolvedDependency, edges);
+        }
+
+        for (String edge : edges) {
+            getTextOutput().println(edge);
+        }
+
+        getTextOutput().println("}");
+    }
+
+    private void buildDotDependencyTree(ResolvedDependency root, Set<String> edges) {
+        if (root.getAllModuleArtifacts().isEmpty()) {
+            return;
+        }
+        for (ResolvedDependency dep : root.getChildren()) {
+            String edge = "\"" + root.toString() + "\" -> \"" + dep.toString().replace('-', '_') + "\";";
+            edges.add(edge);
+        }
+
+        for (ResolvedDependency dep : root.getChildren()) {
+            buildDotDependencyTree(dep, edges);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/PropertyReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/PropertyReportRenderer.java
new file mode 100644
index 0000000..c05c1ad
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/PropertyReportRenderer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+/**
+ * <p>A {@code PropertyReportRenderer} is responsible for rendering the model of a property report.</p>
+ */
+public class PropertyReportRenderer extends TextReportRenderer {
+
+    /**
+     * Writes a property for the current project.
+     *
+     * @param name The name of the property
+     * @param value The value of the property
+     */
+    public void addProperty(String name, Object value) {
+        getTextOutput().formatln("%s: %s", name, value);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java
new file mode 100644
index 0000000..084f92c
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/ReportRenderer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Project;
+import org.gradle.logging.StyledTextOutput;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * <p>A {@code ProjectReportRenderer} is responsible for rendering the model of a project report.</p>
+ */
+public interface ReportRenderer {
+    /**
+     * Sets the text output for the report. This method must be called before any other methods on this renderer.
+     *
+     * @param textOutput The text output, never null.
+     */
+    void setOutput(StyledTextOutput textOutput);
+
+    /**
+     * Sets the output file for the report. This method must be called before any other methods on this renderer.
+     *
+     * @param file The output file, never null.
+     */
+    void setOutputFile(File file) throws IOException;
+
+    /**
+     * Starts visiting a project.
+     *
+     * @param project The project, never null.
+     */
+    void startProject(Project project);
+
+    /**
+     * Completes visiting a project.
+     *
+     * @param project The project, never null.
+     */
+    void completeProject(Project project);
+
+    /**
+     * Completes this report. This method must be called last on this renderer.
+     */
+    void complete() throws IOException;
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/SingleProjectTaskReportModel.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/SingleProjectTaskReportModel.java
new file mode 100644
index 0000000..03affcf
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/SingleProjectTaskReportModel.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.TreeMultimap;
+import org.gradle.api.Task;
+import org.gradle.api.internal.DirectedGraph;
+import org.gradle.api.internal.GraphAggregator;
+import org.gradle.util.GUtil;
+import org.gradle.util.Path;
+
+import java.util.*;
+
+public class SingleProjectTaskReportModel implements TaskReportModel {
+    private final SetMultimap<String, TaskDetails> groups = TreeMultimap.create(new Comparator<String>() {
+        public int compare(String string1, String string2) {
+            return string1.compareToIgnoreCase(string2);
+        }
+    }, new Comparator<TaskDetails>() {
+        public int compare(TaskDetails task1, TaskDetails task2) {
+            return task1.getPath().compareTo(task2.getPath());
+        }
+    });
+    private final TaskDetailsFactory factory;
+
+    public SingleProjectTaskReportModel(TaskDetailsFactory factory) {
+        this.factory = factory;
+    }
+
+    public void build(final Collection<? extends Task> tasks) {
+        Set<Task> topLevelTasks = new LinkedHashSet<Task>();
+        for (final Task task : tasks) {
+            if (GUtil.isTrue(task.getGroup())) {
+                topLevelTasks.add(task);
+            }
+        }
+        GraphAggregator<Task> aggregator = new GraphAggregator<Task>(new DirectedGraph<Task, Object>() {
+            public void getNodeValues(Task node, Collection<Object> values, Collection<Task> connectedNodes) {
+                for (Task dep : node.getTaskDependencies().getDependencies(node)) {
+                    if (tasks.contains(dep)) {
+                        connectedNodes.add(dep);
+                    }
+                }
+            }
+        });
+
+        GraphAggregator.Result<Task> result = aggregator.group(topLevelTasks, tasks);
+        for (Task task : result.getTopLevelNodes()) {
+            Set<Task> nodesForThisTask = new TreeSet<Task>(result.getNodes(task));
+            Set<TaskDetails> children = new LinkedHashSet<TaskDetails>();
+            Set<TaskDetails> dependencies = new LinkedHashSet<TaskDetails>();
+            for (Task node : nodesForThisTask) {
+                if (node != task) {
+                    children.add(new TaskDetailsImpl(node, factory.create(node), Collections.<TaskDetails>emptySet(),
+                            Collections.<TaskDetails>emptySet()));
+                }
+                for (Task dep : node.getTaskDependencies().getDependencies(node)) {
+                    if (topLevelTasks.contains(dep) || !tasks.contains(dep)) {
+                        dependencies.add(factory.create(dep));
+                    }
+                }
+            }
+
+            String group = topLevelTasks.contains(task) ? task.getGroup() : DEFAULT_GROUP;
+            groups.put(group, new TaskDetailsImpl(task, factory.create(task), children, dependencies));
+        }
+    }
+
+    public Set<String> getGroups() {
+        return groups.keySet();
+    }
+
+    public Set<TaskDetails> getTasksForGroup(String group) {
+        if (!groups.containsKey(group)) {
+            throw new IllegalArgumentException(String.format("Unknown group '%s'", group));
+        }
+        return groups.get(group);
+    }
+
+    private static class TaskDetailsImpl implements TaskDetails {
+        private final Task task;
+        private final TaskDetails details;
+        private final Set<TaskDetails> children;
+        private final Set<TaskDetails> dependencies;
+
+        public TaskDetailsImpl(Task task, TaskDetails details, Set<TaskDetails> children, Set<TaskDetails> dependencies) {
+            this.task = task;
+            this.details = details;
+            this.children = children;
+            this.dependencies = dependencies;
+        }
+
+        public Path getPath() {
+            return details.getPath();
+        }
+
+        public String getDescription() {
+            return details.getDescription();
+        }
+
+        @Override
+        public String toString() {
+            return task.toString();
+        }
+
+        public Task getTask() {
+            return task;
+        }
+
+        public Set<TaskDetails> getDependencies() {
+            return dependencies;
+        }
+
+        public Set<TaskDetails> getChildren() {
+            return children;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetails.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetails.java
new file mode 100644
index 0000000..4a3b80b
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetails.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.util.Path;
+
+import java.util.Set;
+
+public interface TaskDetails {
+    Path getPath();
+
+    String getDescription();
+
+    Set<TaskDetails> getDependencies();
+
+    Set<TaskDetails> getChildren();
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetailsFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetailsFactory.java
new file mode 100644
index 0000000..8183260
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetailsFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.util.Path;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class TaskDetailsFactory {
+    private final Set<Project> projects;
+    private final Project project;
+
+    public TaskDetailsFactory(Project project) {
+        this.project = project;
+        this.projects = project.getAllprojects();
+    }
+
+    public TaskDetails create(final Task task) {
+        final String path;
+        Project project = task.getProject();
+        if (projects.contains(project)) {
+            path = this.project.relativeProjectPath(task.getPath());
+        } else {
+            path = task.getPath();
+        }
+        return new TaskDetails() {
+            public Path getPath() {
+                return Path.path(path);
+            }
+
+            public String getDescription() {
+                return task.getDescription();
+            }
+
+            public Set<TaskDetails> getDependencies() {
+                return Collections.emptySet();
+            }
+
+            public Set<TaskDetails> getChildren() {
+                return Collections.emptySet();
+            }
+        };
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportModel.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportModel.java
new file mode 100644
index 0000000..556b3bf
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportModel.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import java.util.Set;
+
+public interface TaskReportModel {
+    String DEFAULT_GROUP = "";
+
+    /**
+     * Returns the task groups which make up this model, in the order that they should be displayed.
+     */
+    Set<String> getGroups();
+
+    /**
+     * Returns the tasks for the given group, in the order that they should be displayed.
+     */
+    Set<TaskDetails> getTasksForGroup(String group);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java
new file mode 100644
index 0000000..770e3d1
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRenderer.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Project;
+import org.gradle.api.Rule;
+import org.gradle.util.GUtil;
+import org.gradle.util.Path;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import static org.gradle.logging.StyledTextOutput.Style.*;
+
+/**
+ * <p>A {@code TaskReportRenderer} is responsible for rendering the model of a project task report.</p>
+ *
+ * @author Hans Dockter
+ */
+public class TaskReportRenderer extends TextReportRenderer {
+    private boolean currentProjectHasTasks;
+    private boolean currentProjectHasRules;
+    private boolean hasContent;
+    private boolean detail;
+
+    @Override
+    public void startProject(Project project) {
+        currentProjectHasTasks = false;
+        currentProjectHasRules = false;
+        hasContent = false;
+        detail = false;
+        super.startProject(project);
+    }
+
+    public void showDetail(boolean detail) {
+        this.detail = detail;
+    }
+    
+    /**
+     * Writes the default task names for the current project.
+     *
+     * @param defaultTaskNames The default task names (must not be null)
+     */
+    public void addDefaultTasks(List<String> defaultTaskNames) {
+        if (defaultTaskNames.size() > 0) {
+            getTextOutput().formatln("Default tasks: %s", GUtil.join(defaultTaskNames, ", "));
+            hasContent = true;
+        }
+    }
+
+    public void startTaskGroup(String taskGroup) {
+        if (!GUtil.isTrue(taskGroup)) {
+            addSubheading("Tasks");
+        } else {
+            addSubheading(StringUtils.capitalize(taskGroup) + " tasks");
+        }
+        currentProjectHasTasks = true;
+    }
+
+    /**
+     * Writes a task for the current project.
+     *
+     * @param task The task
+     */
+    public void addTask(TaskDetails task) {
+        writeTask(task, "");
+    }
+
+    public void addChildTask(TaskDetails task) {
+        if (detail) {
+            writeTask(task, "    ");
+        }
+    }
+
+    private void writeTask(TaskDetails task, String prefix) {
+        getTextOutput().text(prefix);
+        getTextOutput().withStyle(Identifier).text(task.getPath());
+        if (GUtil.isTrue(task.getDescription())) {
+            getTextOutput().withStyle(Description).format(" - %s", task.getDescription());
+        }
+        if (detail) {
+            SortedSet<Path> sortedDependencies = new TreeSet<Path>();
+            for (TaskDetails dependency : task.getDependencies()) {
+                sortedDependencies.add(dependency.getPath());
+            }
+            if (sortedDependencies.size() > 0) {
+                getTextOutput().withStyle(Info).format(" [%s]", GUtil.join(sortedDependencies, ", "));
+            }
+        }
+        getTextOutput().println();
+    }
+
+    private void addSubheading(String header) {
+        if (hasContent) {
+            getTextOutput().println();
+        }
+        hasContent = true;
+        writeSubheading(header);
+    }
+
+    /**
+     * Marks the end of the tasks for the current project.
+     */
+    public void completeTasks() {
+        if (!currentProjectHasTasks) {
+            getTextOutput().withStyle(Info).println("No tasks");
+            hasContent = true;
+        }
+    }
+
+    /**
+     * Writes a rule for the current project.
+     *
+     * @param rule The rule
+     */
+    public void addRule(Rule rule) {
+        if (!currentProjectHasRules) {
+            addSubheading("Rules");
+        }
+        getTextOutput().println(GUtil.elvis(rule.getDescription(), ""));
+        currentProjectHasRules = true;
+    }
+
+    @Override
+    public void complete() throws IOException {
+        if (!detail) {
+            getTextOutput().println();
+            getTextOutput().text("To see all tasks and more detail, run with ").style(UserInput).text("--all").style(Normal).text(".");
+            getTextOutput().println();
+        }
+        super.complete();
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java
new file mode 100644
index 0000000..45e9149
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRenderer.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Project;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.internal.StreamingStyledTextOutput;
+import org.gradle.util.GUtil;
+
+import java.io.*;
+
+import static org.gradle.logging.StyledTextOutput.Style.Header;
+import static org.gradle.logging.StyledTextOutput.Style.Normal;
+
+/**
+ * <p>A basic {@link ReportRenderer} which writes out a text report.
+ */
+public class TextReportRenderer implements ReportRenderer {
+    public static final String SEPARATOR = "------------------------------------------------------------";
+    private StyledTextOutput textOutput;
+    private boolean close;
+
+    public void setOutput(StyledTextOutput textOutput) {
+        setWriter(textOutput, false);
+    }
+
+    public void setOutputFile(File file) throws IOException {
+        cleanupWriter();
+        setWriter(new StreamingStyledTextOutput(new BufferedWriter(new FileWriter(file))), true);
+    }
+
+    public void startProject(Project project) {
+        String header;
+        if (project.getRootProject() == project) {
+            header = "Root Project";
+        } else {
+            header = String.format("Project %s", project.getPath());
+        }
+        if (GUtil.isTrue(project.getDescription())) {
+            header = header + " - " + project.getDescription();
+        }
+        writeHeading(header);
+    }
+
+    public void completeProject(Project project) {
+    }
+
+    public void complete() throws IOException {
+        cleanupWriter();
+    }
+
+    private void setWriter(StyledTextOutput styledTextOutput, boolean close) {
+        this.textOutput = styledTextOutput;
+        this.close = close;
+    }
+
+    private void cleanupWriter() throws IOException {
+        try {
+            if (textOutput != null && close) {
+                ((Closeable) textOutput).close();
+            }
+        } finally {
+            textOutput = null;
+        }
+    }
+
+    public StyledTextOutput getTextOutput() {
+        return textOutput;
+    }
+
+    public void writeHeading(String heading) {
+        textOutput.println().style(Header);
+        textOutput.println(SEPARATOR);
+        textOutput.println(heading);
+        textOutput.text(SEPARATOR);
+        textOutput.style(Normal);
+        textOutput.println().println();
+    }
+
+    public void writeSubheading(String heading) {
+        getTextOutput().style(Header).println(heading);
+        for (int i = 0; i < heading.length(); i++) {
+            getTextOutput().text("-");
+        }
+        getTextOutput().style(Normal).println();
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy
index b259099..6a1f26f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/PatternSet.groovy
@@ -27,6 +27,7 @@ import org.gradle.api.specs.NotSpec
 import org.gradle.api.specs.OrSpec
 import org.apache.tools.ant.DirectoryScanner
 import org.gradle.api.file.FileTreeElement
+import org.gradle.api.internal.file.RelativePathSpec
 
 /**
  * @author Hans Dockter
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/RelativePathSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/RelativePathSpec.java
deleted file mode 100644
index bd7990a..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/api/tasks/util/RelativePathSpec.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.util;
-
-import org.gradle.api.file.FileTreeElement;
-import org.gradle.api.file.RelativePath;
-import org.gradle.api.specs.Spec;
-
-class RelativePathSpec implements Spec<FileTreeElement> {
-    private final Spec<? super RelativePath> pathSpec;
-
-    public RelativePathSpec(Spec<? super RelativePath> pathSpec) {
-        this.pathSpec = pathSpec;
-    }
-
-    public boolean isSatisfiedBy(FileTreeElement element) {
-        return pathSpec.isSatisfiedBy(element.getRelativePath());
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/BuildConfigurer.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/BuildConfigurer.groovy
deleted file mode 100644
index 4d88adc..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/BuildConfigurer.groovy
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.configuration
-
-import org.gradle.api.Project
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.util.Clock
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.gradle.api.Action
-
-/**
- * @author Hans Dockter
- */
-class BuildConfigurer {
-    private static Logger logger = LoggerFactory.getLogger(BuildConfigurer)
-
-    ProjectDependencies2TaskResolver projectDependencies2TasksResolver
-
-    Action<Project> projectEvaluateAction
-
-    BuildConfigurer() {}
-
-    BuildConfigurer(ProjectDependencies2TaskResolver projectDependencies2TasksResolver) {
-        this.projectDependencies2TasksResolver = projectDependencies2TasksResolver
-        projectEvaluateAction = {ProjectInternal project ->
-            project.evaluate()
-        } as Action
-    }
-
-    void process(Project rootProject) {
-        logger.debug('Configuring Project objects')
-        Clock clock = new Clock()
-        rootProject.allprojects(projectEvaluateAction)
-        projectDependencies2TasksResolver.resolve(rootProject)
-        logger.debug("Timing: Configuring projects took " + clock.time)
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/BuildConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/BuildConfigurer.java
new file mode 100644
index 0000000..066a658
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/BuildConfigurer.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration;
+
+import org.gradle.api.internal.GradleInternal;
+
+public interface BuildConfigurer {
+    void configure(GradleInternal gradleInternal);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
new file mode 100644
index 0000000..bed7142
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultBuildConfigurer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class DefaultBuildConfigurer implements BuildConfigurer {
+    private List<Action<? super ProjectInternal>> actions;
+
+    public DefaultBuildConfigurer(Action<? super ProjectInternal>... actions) {
+        this.actions = new ArrayList<Action<? super ProjectInternal>>(Arrays.asList(actions));
+    }
+
+    public void configure(GradleInternal gradle) {
+        gradle.getRootProject().allprojects(new Action<Project>() {
+            public void execute(Project project) {
+                for (Action<? super ProjectInternal> action : actions) {
+                    action.execute((ProjectInternal) project);
+                }
+            }
+        });
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
index 022c381..8ba7773 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/DefaultScriptPluginFactory.java
@@ -16,6 +16,7 @@
 
 package org.gradle.configuration;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.artifacts.dsl.BuildScriptClasspathScriptTransformer;
 import org.gradle.api.internal.artifacts.dsl.BuildScriptTransformer;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
@@ -23,7 +24,6 @@ import org.gradle.api.internal.initialization.ScriptHandlerFactory;
 import org.gradle.api.internal.initialization.ScriptHandlerInternal;
 import org.gradle.api.internal.project.DefaultServiceRegistry;
 import org.gradle.groovy.scripts.*;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 
 public class DefaultScriptPluginFactory implements ScriptPluginFactory {
@@ -31,13 +31,13 @@ public class DefaultScriptPluginFactory implements ScriptPluginFactory {
     private final ImportsReader importsReader;
     private final ScriptHandlerFactory scriptHandlerFactory;
     private final ClassLoader defaultClassLoader;
-    private final LoggingManagerFactory loggingManagerFactory;
+    private final Factory<? extends LoggingManagerInternal> loggingManagerFactory;
 
     public DefaultScriptPluginFactory(ScriptCompilerFactory scriptCompilerFactory,
                                                 ImportsReader importsReader,
                                                 ScriptHandlerFactory scriptHandlerFactory,
                                                 ClassLoader defaultClassLoader,
-                                                LoggingManagerFactory loggingManagerFactory) {
+                                                Factory<? extends LoggingManagerInternal> loggingManagerFactory) {
         this.scriptCompilerFactory = scriptCompilerFactory;
         this.importsReader = importsReader;
         this.scriptHandlerFactory = scriptHandlerFactory;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/GradleLauncherMetaData.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/GradleLauncherMetaData.java
new file mode 100644
index 0000000..7634f3f
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/GradleLauncherMetaData.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration;
+
+import org.gradle.util.UncheckedException;
+
+import java.io.IOException;
+
+public class GradleLauncherMetaData {
+    public void describeCommand(Appendable output, String... args) {
+        try {
+            String appName = System.getProperty("org.gradle.appname", "gradle");
+            output.append(appName);
+            for (String arg : args) {
+                output.append(' ');
+                output.append(arg);
+            }
+        } catch (IOException e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/Help.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/Help.java
new file mode 100644
index 0000000..f4309b2
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/Help.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.util.GradleVersion;
+
+import static org.gradle.logging.StyledTextOutput.Style.UserInput;
+
+public class Help extends DefaultTask {
+    @TaskAction
+    void displayHelp() {
+        StyledTextOutput output = getServices().get(StyledTextOutputFactory.class).create(Help.class);
+        GradleLauncherMetaData metaData = new GradleLauncherMetaData();
+
+        output.println();
+        output.formatln("Welcome to Gradle %s.", new GradleVersion().getVersion());
+        output.println();
+        output.text("To run a build, run ");
+        metaData.describeCommand(output.withStyle(UserInput), "<task> ...");
+        output.println();
+        output.println();
+        output.text("To see a list of available tasks, run ");
+        metaData.describeCommand(output.withStyle(UserInput), "tasks");
+        output.println();
+        output.println();
+        output.text("To see a list of command-line options, run ");
+        metaData.describeCommand(output.withStyle(UserInput), "-?");
+        output.println();
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java
new file mode 100644
index 0000000..3470ec6
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ImplicitTasksConfigurer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
+import org.gradle.api.tasks.diagnostics.DependencyReportTask;
+import org.gradle.api.tasks.diagnostics.ProjectReportTask;
+import org.gradle.api.tasks.diagnostics.PropertyReportTask;
+import org.gradle.api.tasks.diagnostics.TaskReportTask;
+
+public class ImplicitTasksConfigurer implements Action<ProjectInternal> {
+    private static final String HELP_GROUP = "help";
+    public static final String HELP_TASK = "help";
+    public static final String PROJECTS_TASK = "projects";
+    public static final String TASKS_TASK = "tasks";
+    public static final String DEPENDENCIES_TASK = "dependencies";
+    public static final String PROPERTIES_TASK = "properties";
+
+    public void execute(ProjectInternal project) {
+        TaskContainerInternal tasks = project.getImplicitTasks();
+
+        Task task = tasks.add(HELP_TASK, Help.class);
+        task.setDescription("Displays a help message");
+        task.setGroup(HELP_GROUP);
+
+        task = tasks.add(PROJECTS_TASK, ProjectReportTask.class);
+        task.setDescription(String.format("Displays the sub-projects of %s.", project));
+        task.setGroup(HELP_GROUP);
+
+        task = tasks.add(TASKS_TASK, TaskReportTask.class);
+        task.setDescription(String.format("Displays the tasks in %s.", project));
+        task.setGroup(HELP_GROUP);
+
+        task = tasks.add(DEPENDENCIES_TASK, DependencyReportTask.class);
+        task.setDescription(String.format("Displays the dependencies of %s.", project));
+        task.setGroup(HELP_GROUP);
+
+        task = tasks.add(PROPERTIES_TASK, PropertyReportTask.class);
+        task.setDescription(String.format("Displays the properties of %s.", project));
+        task.setGroup(HELP_GROUP);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.java
index 0f58513..0ca6137 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectDependencies2TaskResolver.java
@@ -25,25 +25,20 @@ import org.slf4j.LoggerFactory;
 /**
  * @author Hans Dockter
  */
-public class ProjectDependencies2TaskResolver {
+public class ProjectDependencies2TaskResolver implements Action<Project> {
     private static Logger logger = LoggerFactory.getLogger(ProjectDependencies2TaskResolver.class);
 
-    public void resolve(Project rootProject) {
-        Action<Project> projectAction = new Action<Project>() {
-            public void execute(Project project) {
-                for (Project dependsOnProject : project.getDependsOnProjects()) {
-                    logger.debug("Checking task dependencies for project: {} dependsOn: {}", project, dependsOnProject);
-                    for (Task task : project.getTasks()) {
-                        String taskName = task.getName();
-                        Task dependentTask = dependsOnProject.getTasks().findByName(taskName);
-                        if (dependentTask != null) {
-                            logger.debug("Setting task dependencies for task: {}", taskName);
-                            task.dependsOn(dependentTask);
-                        }
-                    }
+    public void execute(Project project) {
+        for (Project dependsOnProject : project.getDependsOnProjects()) {
+            logger.debug("Checking task dependencies for project: {} dependsOn: {}", project, dependsOnProject);
+            for (Task task : project.getTasks()) {
+                String taskName = task.getName();
+                Task dependentTask = dependsOnProject.getTasks().findByName(taskName);
+                if (dependentTask != null) {
+                    logger.debug("Setting task dependencies for task: {}", taskName);
+                    task.dependsOn(dependentTask);
                 }
             }
-        };
-        rootProject.allprojects(projectAction);
+        }
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectEvaluationConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectEvaluationConfigurer.java
new file mode 100644
index 0000000..2822298
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/configuration/ProjectEvaluationConfigurer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.project.ProjectInternal;
+
+public class ProjectEvaluationConfigurer implements Action<ProjectInternal> {
+    public void execute(ProjectInternal projectInternal) {
+        projectInternal.evaluate();
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/BuiltInTaskBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/BuiltInTaskBuildExecuter.java
deleted file mode 100644
index ec789e3..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/BuiltInTaskBuildExecuter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution;
-
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.taskfactory.ITaskFactory;
-import org.gradle.api.tasks.diagnostics.AbstractReportTask;
-import org.gradle.util.WrapUtil;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A {@link BuildExecuter} which executes the built-in tasks which are executable from the command-line.
- */
-public abstract class BuiltInTaskBuildExecuter<T extends AbstractReportTask> implements BuildExecuter {
-    private GradleInternal gradle;
-    public static final String ALL_PROJECTS_WILDCARD = "?";
-
-    private T task;
-    private String path;
-
-    public BuiltInTaskBuildExecuter(String path) {
-        this.path = path;
-    }
-
-    protected abstract Class<T> getTaskType();
-
-    public void select(GradleInternal gradle) {
-        this.gradle = gradle;
-        ITaskFactory taskFactory = gradle.getServiceRegistryFactory().get(ITaskFactory.class);
-        Map<String, Object> args = new HashMap<String, Object>();
-        args.put(Task.TASK_NAME, "report");
-        args.put(Task.TASK_TYPE, getTaskType());
-        task = getTaskType().cast(taskFactory.createTask(gradle.getDefaultProject(), args));
-        task.setProjects(getProjectsForReport(path));
-        afterConfigure(task);
-    }
-
-    protected void afterConfigure(T task) {
-    }
-
-    private Set<Project> getProjectsForReport(String path) {
-        if (path != null) {
-            return path.equals(ALL_PROJECTS_WILDCARD) ? gradle.getRootProject().getAllprojects() : WrapUtil.toSet(
-                    gradle.getRootProject().project(path));
-        }
-        return WrapUtil.<Project>toSet(gradle.getDefaultProject());
-    }
-
-    public T getTask() {
-        return task;
-    }
-
-    public void execute() {
-        gradle.getTaskGraph().execute(Collections.singleton(task));
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java
index eb509e9..684f9da 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DefaultBuildExecuter.java
@@ -18,7 +18,6 @@ package org.gradle.execution;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.Task;
 import org.gradle.api.specs.Spec;
-import org.gradle.util.GUtil;
 
 import java.util.*;
 
@@ -41,7 +40,11 @@ public class DefaultBuildExecuter extends DelegatingBuildExecuter {
     public void select(GradleInternal gradle) {
         if (!excludedTaskNames.isEmpty()) {
             final Set<Task> excludedTasks = new HashSet<Task>();
-            GUtil.flatten(TaskNameResolvingBuildExecuter.select(gradle, excludedTaskNames), excludedTasks);
+            TaskSelector selector = new TaskSelector();
+            for (String taskName : excludedTaskNames) {
+                selector.selectTasks(gradle, taskName);
+                excludedTasks.addAll(selector.getTasks());
+            }
             gradle.getTaskGraph().useFilter(new Spec<Task>() {
                 public boolean isSatisfiedBy(Task task) {
                     return !excludedTasks.contains(task);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DependencyReportBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DependencyReportBuildExecuter.java
deleted file mode 100644
index 94c79b8..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/DependencyReportBuildExecuter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution;
-
-import org.gradle.api.tasks.diagnostics.DependencyReportTask;
-
-public class DependencyReportBuildExecuter extends BuiltInTaskBuildExecuter<DependencyReportTask> {
-    public DependencyReportBuildExecuter(String path) {
-        super(path);
-    }
-
-    @Override
-    protected Class<DependencyReportTask> getTaskType() {
-        return DependencyReportTask.class;
-    }
-
-    public String getDisplayName() {
-        return "dependency list";
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/ProjectDefaultsBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/ProjectDefaultsBuildExecuter.java
index 9a15dcd..b87166a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/ProjectDefaultsBuildExecuter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/ProjectDefaultsBuildExecuter.java
@@ -17,8 +17,10 @@ package org.gradle.execution;
 
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.util.GUtil;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -26,15 +28,17 @@ import java.util.List;
  */
 public class ProjectDefaultsBuildExecuter extends DelegatingBuildExecuter {
     private List<String> defaultTasks;
+    private String displayName;
 
     public void select(GradleInternal gradle) {
         if (getDelegate() == null) {
             // Gather the default tasks from this first group project
             ProjectInternal project = gradle.getDefaultProject();
             defaultTasks = project.getDefaultTasks();
+            displayName = String.format("project default tasks %s", GUtil.toString(defaultTasks));
             if (defaultTasks.size() == 0) {
-                throw new TaskSelectionException(String.format(
-                        "No tasks have been specified and %s has not defined any default tasks.", project));
+                defaultTasks = Arrays.asList(ImplicitTasksConfigurer.HELP_TASK);
+                displayName = String.format("default task %s", GUtil.toString(defaultTasks));
             }
             setDelegate(new TaskNameResolvingBuildExecuter(defaultTasks));
         }
@@ -44,6 +48,6 @@ public class ProjectDefaultsBuildExecuter extends DelegatingBuildExecuter {
 
     @Override
     public String getDisplayName() {
-        return String.format("project default tasks %s", GUtil.toString(defaultTasks));
+        return displayName;
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/PropertyReportBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/PropertyReportBuildExecuter.java
deleted file mode 100644
index 5fc26d7..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/PropertyReportBuildExecuter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution;
-
-import org.gradle.api.tasks.diagnostics.PropertyReportTask;
-
-public class PropertyReportBuildExecuter extends BuiltInTaskBuildExecuter<PropertyReportTask> {
-    public PropertyReportBuildExecuter(String path) {
-        super(path);
-    }
-
-    @Override
-    protected Class<PropertyReportTask> getTaskType() {
-        return PropertyReportTask.class;
-    }
-
-    public String getDisplayName() {
-        return "property list";
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolver.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
new file mode 100644
index 0000000..424ae41
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolver.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.execution;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectInternal;
+
+import java.util.Collections;
+
+public class TaskNameResolver {
+    
+    public SetMultimap<String, Task> select(String name, Project project) {
+        return select(name, (ProjectInternal) project, Collections.<Project>emptySet());
+    }
+
+    public SetMultimap<String, Task> selectAll(String name, Project project) {
+        return select(name, (ProjectInternal) project, project.getSubprojects());
+    }
+
+    private SetMultimap<String, Task> select(String name, ProjectInternal project, Iterable<Project> additionalProjects) {
+        SetMultimap<String, Task> selected = LinkedHashMultimap.create();
+        Task task = project.getTasks().findByName(name);
+        if (task != null) {
+            selected.put(task.getName(), task);
+        } else {
+            task = project.getImplicitTasks().findByName(name);
+            if (task != null) {
+                selected.put(task.getName(), task);
+            }
+        }
+        for (Project additionalProject : additionalProjects) {
+            task = additionalProject.getTasks().findByName(name);
+            if (task != null) {
+                selected.put(task.getName(), task);
+            }
+        }
+        if (!selected.isEmpty()) {
+            return selected;
+        }
+
+        for (Task t : project.getTasks()) {
+            selected.put(t.getName(), t);
+        }
+        for (Task t : project.getImplicitTasks()) {
+            if (!selected.containsKey(t.getName())) {
+                selected.put(t.getName(), t);
+            }
+        }
+        for (Project additionalProject : additionalProjects) {
+            for (Task t : additionalProject.getTasks()) {
+                selected.put(t.getName(), t);
+            }
+        }
+
+        return selected;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildExecuter.java
index 86e6cfe..1208d09 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildExecuter.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskNameResolvingBuildExecuter.java
@@ -15,13 +15,18 @@
  */
 package org.gradle.execution;
 
-import org.apache.commons.lang.StringUtils;
-import org.gradle.api.Project;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
 import org.gradle.api.Task;
 import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.tasks.CommandLineOption;
+import org.gradle.initialization.CommandLineParser;
+import org.gradle.initialization.ParsedCommandLine;
 import org.gradle.util.GUtil;
-import org.gradle.util.NameMatcher;
+import org.gradle.util.JavaMethod;
 
+import java.lang.reflect.Method;
 import java.util.*;
 
 /**
@@ -32,127 +37,74 @@ public class TaskNameResolvingBuildExecuter implements BuildExecuter {
     private final List<String> names;
     private String description;
     private TaskGraphExecuter executer;
+    private final TaskNameResolver taskNameResolver;
 
     public TaskNameResolvingBuildExecuter(Collection<String> names) {
+        this(names, new TaskNameResolver());
+    }
+
+    TaskNameResolvingBuildExecuter(Collection<String> names, TaskNameResolver taskNameResolver) {
+        this.taskNameResolver = taskNameResolver;
         this.names = new ArrayList<String>(names);
     }
 
+    public List<String> getNames() {
+        return names;
+    }
+
     public void select(GradleInternal gradle) {
-        Map<String, Collection<Task>> selectedTasks = doSelect(gradle, names);
+        Multimap<String, Task> selectedTasks = doSelect(gradle, names, taskNameResolver);
 
         this.executer = gradle.getTaskGraph();
-        for (Collection<Task> tasksForName : selectedTasks.values()) {
-            executer.addTasks(tasksForName);
+        for (String name : selectedTasks.keySet()) {
+            executer.addTasks(selectedTasks.get(name));
         }
-        if (selectedTasks.size() == 1) {
+        if (selectedTasks.keySet().size() == 1) {
             description = String.format("primary task %s", GUtil.toString(selectedTasks.keySet()));
         } else {
             description = String.format("primary tasks %s", GUtil.toString(selectedTasks.keySet()));
         }
     }
 
-    static List<Collection<Task>> select(GradleInternal gradle, Iterable<String> names) {
-        return new ArrayList<Collection<Task>>(doSelect(gradle, names).values());
-    }
-
-    private static Map<String, Collection<Task>> doSelect(GradleInternal gradle, Iterable<String> paths) {
-
-        Map<String, Collection<Task>> allProjectsTasksByName = null;
-
-        Map<String, Collection<Task>> matches = new LinkedHashMap<String, Collection<Task>>();
-        for (String path : paths) {
-            Map<String, Collection<Task>> tasksByName;
-            String baseName;
-            String prefix;
-
-            Project project = gradle.getDefaultProject();
-
-            if (path.contains(Project.PATH_SEPARATOR)) {
-                String projectPath = StringUtils.substringBeforeLast(path, Project.PATH_SEPARATOR);
-                projectPath = projectPath.length() == 0 ? Project.PATH_SEPARATOR : projectPath;
-                project = findProject(project, projectPath);
-                baseName = StringUtils.substringAfterLast(path, Project.PATH_SEPARATOR);
-                Task match = project.getTasks().findByName(baseName);
-                if (match != null) {
-                    matches.put(path, Collections.singleton(match));
-                    continue;
-                }
-
-                tasksByName = new HashMap<String, Collection<Task>>();
-                for (Task task : project.getTasks().getAll()) {
-                    tasksByName.put(task.getName(), Collections.singleton(task));
-                }
-
-                prefix = project.getPath() + Project.PATH_SEPARATOR;
-            }
-            else {
-                Set<Task> tasks = project.getTasksByName(path, true);
-                if (!tasks.isEmpty()) {
-                    matches.put(path, tasks);
-                    continue;
-                }
-                if (allProjectsTasksByName == null) {
-                    allProjectsTasksByName = buildTaskMap(project);
+    private Multimap<String, Task> doSelect(GradleInternal gradle, List<String> paths, TaskNameResolver taskNameResolver) {
+        SetMultimap<String, Task> matches = LinkedHashMultimap.create();
+        TaskSelector selector = new TaskSelector(taskNameResolver);
+        List<String> remainingPaths = paths;
+        while (!remainingPaths.isEmpty()) {
+            String path = remainingPaths.get(0);
+            selector.selectTasks(gradle, path);
+
+            CommandLineParser commandLineParser = new CommandLineParser();
+            Set<Task> tasks = selector.getTasks();
+            Map<String, JavaMethod<Task, ?>> options = new HashMap<String, JavaMethod<Task, ?>>();
+            if (tasks.size() == 1) {
+                for (Class<?> type = tasks.iterator().next().getClass(); type != Object.class; type = type.getSuperclass()) {
+                    for (Method method : type.getDeclaredMethods()) {
+                        CommandLineOption commandLineOption = method.getAnnotation(CommandLineOption.class);
+                        if (commandLineOption != null) {
+                            commandLineParser.option(commandLineOption.options()).hasDescription(commandLineOption.description());
+                            options.put(commandLineOption.options()[0], new JavaMethod<Task, Object>(Task.class, Object.class, method));
+                        }
+                    }
                 }
-                tasksByName = allProjectsTasksByName;
-                baseName = path;
-                prefix = "";
             }
 
-            NameMatcher matcher = new NameMatcher();
-            String actualName = matcher.find(baseName, tasksByName.keySet());
-
-            if (actualName != null) {
-                matches.put(prefix + actualName, tasksByName.get(actualName));
-                continue;
+            ParsedCommandLine commandLine = commandLineParser.parse(remainingPaths.subList(1, remainingPaths.size()));
+            for (Map.Entry<String, JavaMethod<Task, ?>> entry : options.entrySet()) {
+                if (commandLine.hasOption(entry.getKey())) {
+                    for (Task task : tasks) {
+                        entry.getValue().invoke(task, true);
+                    }
+                }
             }
+            remainingPaths = commandLine.getExtraArguments();
 
-            throw new TaskSelectionException(matcher.formatErrorMessage("task", project));
+            matches.putAll(selector.getTaskName(), tasks);
         }
 
         return matches;
     }
 
-    private static Project findProject(Project startFrom, String path) {
-        if (path.equals(Project.PATH_SEPARATOR)) {
-            return startFrom.getRootProject();
-        }
-        Project current = startFrom;
-        if (path.startsWith(Project.PATH_SEPARATOR)) {
-            current = current.getRootProject();
-            path = path.substring(1);
-        }
-        for (String pattern : path.split(Project.PATH_SEPARATOR)) {
-            Map<String, Project> children = current.getChildProjects();
-
-            NameMatcher matcher = new NameMatcher();
-            Project child = matcher.find(pattern, children);
-            if (child != null) {
-                current = child;
-                continue;
-            }
-
-            throw new TaskSelectionException(matcher.formatErrorMessage("project", current));
-        }
-
-        return current;
-    }
-
-    private static Map<String, Collection<Task>> buildTaskMap(Project defaultProject) {
-        Map<String, Collection<Task>> tasksByName = new HashMap<String, Collection<Task>>();
-        for (Project project : defaultProject.getAllprojects()) {
-            for (Task task : project.getTasks().getAll()) {
-                Collection<Task> tasksForName = tasksByName.get(task.getName());
-                if (tasksForName == null) {
-                    tasksForName = new HashSet<Task>();
-                    tasksByName.put(task.getName(), tasksForName);
-                }
-                tasksForName.add(task);
-            }
-        }
-        return tasksByName;
-    }
-
     public String getDisplayName() {
         return description;
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskReportBuildExecuter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskReportBuildExecuter.java
deleted file mode 100644
index c241790..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskReportBuildExecuter.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution;
-
-import org.gradle.api.tasks.diagnostics.TaskReportTask;
-
-public class TaskReportBuildExecuter extends BuiltInTaskBuildExecuter<TaskReportTask> {
-    private final boolean showDetail;
-
-    public TaskReportBuildExecuter(String path, boolean showDetail) {
-        super(path);
-        this.showDetail = showDetail;
-    }
-
-    @Override
-    protected Class<TaskReportTask> getTaskType() {
-        return TaskReportTask.class;
-    }
-
-    public String getDisplayName() {
-        return "task list";
-    }
-
-    @Override
-    protected void afterConfigure(TaskReportTask task) {
-        task.setShowDetail(showDetail);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskSelector.java b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskSelector.java
new file mode 100644
index 0000000..1e73abc
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/execution/TaskSelector.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.execution;
+
+import com.google.common.collect.SetMultimap;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.util.NameMatcher;
+
+import java.util.Map;
+import java.util.Set;
+
+public class TaskSelector {
+    private final TaskNameResolver taskNameResolver;
+    private Set<Task> tasks;
+    private String taskName;
+
+    public TaskSelector() {
+        this(new TaskNameResolver());
+    }
+
+    public TaskSelector(TaskNameResolver taskNameResolver) {
+        this.taskNameResolver = taskNameResolver;
+    }
+
+    public void selectTasks(GradleInternal gradle, String path) {
+        SetMultimap<String, Task> tasksByName;
+        String baseName;
+        String prefix;
+
+        ProjectInternal project = gradle.getDefaultProject();
+
+        if (path.contains(Project.PATH_SEPARATOR)) {
+            String projectPath = StringUtils.substringBeforeLast(path, Project.PATH_SEPARATOR);
+            projectPath = projectPath.length() == 0 ? Project.PATH_SEPARATOR : projectPath;
+            project = findProject(project, projectPath);
+            baseName = StringUtils.substringAfterLast(path, Project.PATH_SEPARATOR);
+            prefix = project.getPath() + Project.PATH_SEPARATOR;
+
+            tasksByName = taskNameResolver.select(baseName, project);
+        }
+        else {
+            baseName = path;
+            prefix = "";
+
+            tasksByName = taskNameResolver.selectAll(path, project);
+        }
+
+        Set<Task> tasks = tasksByName.get(baseName);
+        if (!tasks.isEmpty()) {
+            // An exact match
+            this.tasks = tasks;
+            this.taskName = path;
+            return;
+        }
+
+        NameMatcher matcher = new NameMatcher();
+        String actualName = matcher.find(baseName, tasksByName.keySet());
+
+        if (actualName != null) {
+            // A partial match
+            this.tasks = tasksByName.get(actualName);
+            this.taskName = prefix + actualName;
+            return;
+        }
+
+        throw new TaskSelectionException(matcher.formatErrorMessage("task", project));
+    }
+
+    public String getTaskName() {
+        return taskName;
+    }
+
+    public Set<Task> getTasks() {
+        return tasks;
+    }
+
+    private static ProjectInternal findProject(ProjectInternal startFrom, String path) {
+        if (path.equals(Project.PATH_SEPARATOR)) {
+            return startFrom.getRootProject();
+        }
+        Project current = startFrom;
+        if (path.startsWith(Project.PATH_SEPARATOR)) {
+            current = current.getRootProject();
+            path = path.substring(1);
+        }
+        for (String pattern : path.split(Project.PATH_SEPARATOR)) {
+            Map<String, Project> children = current.getChildProjects();
+
+            NameMatcher matcher = new NameMatcher();
+            Project child = matcher.find(pattern, children);
+            if (child != null) {
+                current = child;
+                continue;
+            }
+
+            throw new TaskSelectionException(matcher.formatErrorMessage("project", current));
+        }
+
+        return (ProjectInternal) current;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/AbstractCommandLineConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/AbstractCommandLineConverter.java
new file mode 100644
index 0000000..8afbef5
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/AbstractCommandLineConverter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import org.gradle.CommandLineArgumentException;
+
+public abstract class AbstractCommandLineConverter<T> implements CommandLineConverter<T> {
+    public T convert(Iterable<String> args) throws CommandLineArgumentException {
+        CommandLineParser parser = new CommandLineParser();
+        configure(parser);
+        return convert(parser.parse(args));
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java
index f7197e0..634da17 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildProgressLogger.java
@@ -36,7 +36,7 @@ class BuildProgressLogger extends BuildAdapter implements TaskExecutionGraphList
     @Override
     public void buildStarted(Gradle gradle) {
         if (gradle.getParent() == null) {
-            progressLogger = progressLoggerFactory.start();
+            progressLogger = progressLoggerFactory.start(BuildProgressLogger.class.getName());
             progressLogger.progress("Loading");
             this.gradle = gradle;
         }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java
index 562ec86..ff013e0 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/BuildSourceBuilder.java
@@ -85,6 +85,7 @@ public class BuildSourceBuilder {
         StartParameter startParameterArg = startParameter.newInstance();
         startParameterArg.setProjectProperties(startParameter.getProjectProperties());
         startParameterArg.setSearchUpwards(false);
+        startParameterArg.setProfile(startParameter.isProfile());
 
         // If we were not the most recent version of Gradle to build the buildSrc dir, then do a clean build
         // Otherwise, just to a regular build
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLine2StartParameterConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLine2StartParameterConverter.java
deleted file mode 100644
index 4e86a8f..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLine2StartParameterConverter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.initialization;
-
-import org.gradle.StartParameter;
-
-import java.io.OutputStream;
-
-/**
- * @author Hans Dockter
- */
-public interface CommandLine2StartParameterConverter {
-    StartParameter convert(String[] args);
-
-    void convert(String[] args, StartParameter parameter);
-
-    void showHelp(OutputStream out);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineConverter.java
new file mode 100644
index 0000000..dd76c54
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineConverter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import org.gradle.CommandLineArgumentException;
+
+/**
+ * @author Hans Dockter
+ */
+public interface CommandLineConverter<T> {
+    T convert(Iterable<String> args) throws CommandLineArgumentException;
+    
+    T convert(ParsedCommandLine args) throws CommandLineArgumentException;
+
+    void configure(CommandLineParser parser);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineOption.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineOption.java
new file mode 100644
index 0000000..d5325aa
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineOption.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import org.gradle.util.GUtil;
+
+import java.util.List;
+import java.util.Set;
+
+public class CommandLineOption {
+    private final Set<String> options;
+    private Class<?> argumentType = Void.TYPE;
+    private String description;
+    private String subcommand;
+
+    public CommandLineOption(Iterable<String> options) {
+        this.options = GUtil.addSets(options);
+    }
+
+    public Set<String> getOptions() {
+        return options;
+    }
+
+    public CommandLineOption hasArgument() {
+        argumentType = String.class;
+        return this;
+    }
+
+    public CommandLineOption hasArguments() {
+        argumentType = List.class;
+        return this;
+    }
+
+    public String getSubcommand() {
+        return subcommand;
+    }
+
+    public CommandLineOption mapsToSubcommand(String command) {
+        this.subcommand = command;
+        return this;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public CommandLineOption hasDescription(String description) {
+        this.description = description;
+        return this;
+    }
+
+    public boolean getAllowsArguments() {
+        return argumentType != Void.TYPE;
+    }
+
+    public boolean getAllowsMultipleArguments() {
+        return argumentType == List.class;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineParser.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineParser.java
new file mode 100644
index 0000000..fd7173d
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/CommandLineParser.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import org.gradle.CommandLineArgumentException;
+import org.gradle.util.GUtil;
+
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * <p>A command-line parser which supports a command/subcommand style command-line interface. Supports the following
+ * syntax:</p>
+ * <pre>
+ * <option>* (<subcommand> <subcommand-option>*)*
+ * </pre>
+ *
+ * <ul> <li>Short options are a '-' followed by a single character. For example: {@code -a}.</li>
+ *
+ * <li>Long options are '--' followed by multiple characters. For example: {@code --long-option}.</li>
+ *
+ * <li>Options can take arguments. The argument follows the option. For example: {@code -a arg} or {@code --long
+ * arg}.</li>
+ *
+ * <li>Arguments can be attached to the option using '='. For example: {@code -a=arg} or {@code --long=arg}.</li>
+ *
+ * <li>Arguments can be attached to short options. For example: {@code -aarg}.</li>
+ *
+ * <li>Short options can be combined. For example {@code -ab} is equivalent to {@code -a -b}.</li>
+ *
+ * <li>Anything else is treated as an extra argument. This includes a single {@code -} character.</li>
+ *
+ * <li>'--' indicates the end of the options. Anything following is not parsed and is treated as extra arguments.</li>
+ *
+ * <li>The parser is forgiving, and allows '--' to be used with short options and '-' to be used with long
+ * options.</li>
+ *
+ * <li>The set of options must be known at parse time. Subcommands and their options do not need to be known at parse
+ * time. Use {@link ParsedCommandLine#getExtraArguments()} to obtain the non-option command-line arguments.</li>
+ *
+ * </ul>
+ */
+public class CommandLineParser {
+    private Map<String, CommandLineOption> optionsByString = new HashMap<String, CommandLineOption>();
+    private boolean allowMixedOptions;
+
+    /**
+     * Parses the given command-line.
+     *
+     * @param commandLine The command-line.
+     * @return The parsed command line.
+     * @throws org.gradle.CommandLineArgumentException
+     *          On parse failure.
+     */
+    public ParsedCommandLine parse(String[] commandLine) throws CommandLineArgumentException {
+        return parse(Arrays.asList(commandLine));
+    }
+
+    /**
+     * Parses the given command-line.
+     *
+     * @param commandLine The command-line.
+     * @return The parsed command line.
+     * @throws org.gradle.CommandLineArgumentException
+     *          On parse failure.
+     */
+    public ParsedCommandLine parse(Iterable<String> commandLine) {
+        ParsedCommandLine parsedCommandLine = new ParsedCommandLine(new HashSet<CommandLineOption>(optionsByString.values()));
+        ParseState parseState = new BeforeSubcommand(parsedCommandLine);
+        for (String arg : commandLine) {
+            if (parseState.maybeStartOption(arg)) {
+                if (arg.equals("--")) {
+                    parseState = new OptionsComplete(parsedCommandLine);
+                } else if (arg.matches("--[^=]+")) {
+                    OptionParseState parsedOption = parseState.addOption(arg, arg.substring(2));
+                    parseState = parsedOption.asNextArg();
+                } else if (arg.matches("--[^=]+=.*")) {
+                    int endArg = arg.indexOf('=');
+                    OptionParseState parsedOption = parseState.addOption(arg, arg.substring(2, endArg));
+                    parsedOption.addArgument(arg.substring(endArg + 1));
+                } else if (arg.matches("-[^=]=.*")) {
+                    OptionParseState parsedOption = parseState.addOption(arg, arg.substring(1, 2));
+                    parsedOption.addArgument(arg.substring(3));
+                } else {
+                    assert arg.matches("-[^-].*");
+                    String option = arg.substring(1);
+                    if (optionsByString.containsKey(option)) {
+                        OptionParseState parsedOption = parseState.addOption(arg, option);
+                        parseState = parsedOption.asNextArg();
+                    } else {
+                        String option1 = arg.substring(1, 2);
+                        OptionParseState parsedOption = parseState.addOption("-" + option1, option1);
+                        if (parsedOption.getHasArgument()) {
+                            parsedOption.addArgument(arg.substring(2));
+                        } else {
+                            parseState = parsedOption.argumentMissing();
+                            for (int i = 2; i < arg.length(); i++) {
+                                String optionStr = arg.substring(i, i + 1);
+                                parsedOption = parseState.addOption("-" + optionStr, optionStr);
+                                parseState = parsedOption.argumentMissing();
+                            }
+                        }
+                    }
+                }
+            } else {
+                parseState = parseState.onExtraValue(arg);
+            }
+        }
+
+        parseState.complete();
+        return parsedCommandLine;
+    }
+
+    public CommandLineParser allowMixedSubcommandsAndOptions() {
+        allowMixedOptions = true;
+        return this;
+    }
+
+    /**
+     * Prints a usage message to the given stream.
+     *
+     * @param out The output stream to write to.
+     */
+    public void printUsage(OutputStream out) {
+        Formatter formatter = new Formatter(out);
+        Set<CommandLineOption> orderedOptions = new TreeSet<CommandLineOption>(new OptionComparator());
+        orderedOptions.addAll(optionsByString.values());
+        Map<String, String> lines = new LinkedHashMap<String, String>();
+        for (CommandLineOption option : orderedOptions) {
+            Set<String> orderedOptionStrings = new TreeSet<String>(new OptionStringComparator());
+            orderedOptionStrings.addAll(option.getOptions());
+            List<String> prefixedStrings = new ArrayList<String>();
+            for (String optionString : orderedOptionStrings) {
+                if (optionString.length() == 1) {
+                    prefixedStrings.add("-" + optionString);
+                } else {
+                    prefixedStrings.add("--" + optionString);
+                }
+            }
+            lines.put(GUtil.join(prefixedStrings, ", "), GUtil.elvis(option.getDescription(), ""));
+        }
+        int max = 0;
+        for (String optionStr : lines.keySet()) {
+            max = Math.max(max, optionStr.length());
+        }
+        for (Map.Entry<String, String> entry : lines.entrySet()) {
+            if (entry.getValue().length() == 0) {
+                formatter.format("%s%n", entry.getKey());
+            } else {
+                formatter.format("%-" + max + "s  %s%n", entry.getKey(), entry.getValue());
+            }
+        }
+        formatter.flush();
+    }
+
+    /**
+     * Defines a new option. By default, the option takes no arguments and has no description.
+     *
+     * @param options The options values.
+     * @return The option, which can be further configured.
+     */
+    public CommandLineOption option(String... options) {
+        for (String option : options) {
+            if (optionsByString.containsKey(option)) {
+                throw new IllegalArgumentException(String.format("Option '%s' is already defined.", option));
+            }
+            if (option.startsWith("-")) {
+                throw new IllegalArgumentException(String.format("Cannot add option '%s' as an option cannot start with '-'.", option));
+            }
+        }
+        CommandLineOption option = new CommandLineOption(Arrays.asList(options));
+        for (String optionStr : option.getOptions()) {
+            this.optionsByString.put(optionStr, option);
+        }
+        return option;
+    }
+
+    private static class OptionString {
+        private final String arg;
+        private final String option;
+
+        private OptionString(String arg, String option) {
+            this.arg = arg;
+            this.option = option;
+        }
+
+        public String getDisplayName() {
+            return arg.startsWith("--") ? "--" + option : "-" + option;
+        }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+    }
+
+    private static abstract class ParseState {
+        public boolean maybeStartOption(String arg) {
+            return arg.matches("-.+");
+        }
+
+        public abstract ParseState onExtraValue(String arg);
+
+        public void complete() {
+        }
+
+        public OptionParseState addOption(String arg, String option) {
+            return addOption(new OptionString(arg, option));
+        }
+
+        public abstract OptionParseState addOption(OptionString option);
+    }
+
+    private static class OptionParseState extends ParseState {
+        private final ParsedCommandLineOption option;
+        private final OptionString actualOption;
+        private final ParseState nextState;
+
+        private OptionParseState(ParsedCommandLineOption option, OptionString actualOption, ParseState nextState) {
+            this.option = option;
+            this.actualOption = actualOption;
+            this.nextState = nextState;
+        }
+
+        @Override
+        public boolean maybeStartOption(String arg) {
+            if (super.maybeStartOption(arg)) {
+                argumentMissing();
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public ParseState onExtraValue(String arg) {
+            addArgument(arg);
+            return nextState;
+        }
+
+        @Override
+        public void complete() {
+            argumentMissing();
+        }
+
+        @Override
+        public OptionParseState addOption(OptionString option) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void addArgument(String argument) {
+            if (!getHasArgument()) {
+                throw new CommandLineArgumentException(String.format("Command-line option '%s' does not take an argument.", actualOption));
+            }
+            if (argument.length() == 0) {
+                throw new CommandLineArgumentException(String.format("An empty argument was provided for command-line option '%s'.", actualOption));
+            }
+            if (!option.getValues().isEmpty() && !option.getOption().getAllowsMultipleArguments()) {
+                throw new CommandLineArgumentException(String.format("Multiple arguments were provided for command-line option '%s'.", actualOption));
+            }
+            option.addArgument(argument);
+        }
+
+        public boolean getHasArgument() {
+            return option.getOption().getAllowsArguments();
+        }
+
+        public ParseState asNextArg() {
+            return getHasArgument() ? this : nextState;
+        }
+
+        public ParseState argumentMissing() {
+            if (!getHasArgument()) {
+                return nextState;
+            }
+            throw new CommandLineArgumentException(String.format("No argument was provided for command-line option '%s'.", actualOption));
+        }
+    }
+
+    private static class GlobalParseState extends ParseState {
+        final ParsedCommandLine commandLine;
+
+        GlobalParseState(ParsedCommandLine commandLine) {
+            this.commandLine = commandLine;
+        }
+
+        @Override
+        public OptionParseState addOption(OptionString option) {
+            ParsedCommandLineOption parsedOption = commandLine.addOption(option.option);
+            if (parsedOption == null) {
+                return onUnknownOption(option);
+            }
+            if (parsedOption.getOption().getSubcommand() != null) {
+                ParseState nextState = onExtraValue(parsedOption.getOption().getSubcommand());
+                return new OptionParseState(parsedOption, option, nextState);
+            }
+            return new OptionParseState(parsedOption, option, this);
+        }
+
+        OptionParseState onUnknownOption(OptionString option) {
+            throw new CommandLineArgumentException(String.format("Unknown command-line option '%s'.", option));
+        }
+
+        @Override
+        public ParseState onExtraValue(String arg) {
+            commandLine.addExtraValue(arg);
+            return this;
+        }
+    }
+
+    private class BeforeSubcommand extends GlobalParseState {
+        BeforeSubcommand(ParsedCommandLine commandLine) {
+            super(commandLine);
+        }
+
+        @Override
+        public ParseState onExtraValue(String arg) {
+            super.onExtraValue(arg);
+            return new AfterSubcommand(commandLine);
+        }
+    }
+
+    private class AfterSubcommand extends GlobalParseState {
+        AfterSubcommand(ParsedCommandLine commandLine) {
+            super(commandLine);
+        }
+
+        @Override
+        public OptionParseState addOption(OptionString option) {
+            if (allowMixedOptions) {
+                return super.addOption(option);
+            } else {
+                return onUnknownOption(option);
+            }
+        }
+
+        @Override
+        OptionParseState onUnknownOption(OptionString option) {
+            commandLine.addExtraValue(option.arg);
+            return new OptionParseState(new ParsedCommandLineOption(new CommandLineOption(Collections.singleton(option.option))), option, this);
+        }
+    }
+
+    private static class OptionsComplete extends GlobalParseState {
+        OptionsComplete(ParsedCommandLine commandLine) {
+            super(commandLine);
+        }
+
+        @Override
+        public boolean maybeStartOption(String arg) {
+            return false;
+        }
+    }
+
+    private static final class OptionComparator implements Comparator<CommandLineOption> {
+        public int compare(CommandLineOption option1, CommandLineOption option2) {
+            String min1 = Collections.min(option1.getOptions(), new OptionStringComparator());
+            String min2 = Collections.min(option2.getOptions(), new OptionStringComparator());
+            return new CaseInsensitiveStringComparator().compare(min1, min2);
+        }
+    }
+
+    private static final class CaseInsensitiveStringComparator implements Comparator<String> {
+        public int compare(String option1, String option2) {
+            int diff = option1.compareToIgnoreCase(option2);
+            if (diff != 0) {
+                return diff;
+            }
+            return option1.compareTo(option2);
+        }
+    }
+
+    private static final class OptionStringComparator implements Comparator<String> {
+        public int compare(String option1, String option2) {
+            boolean short1 = option1.length() == 1;
+            boolean short2 = option2.length() == 1;
+            if (short1 && !short2) {
+                return -1;
+            }
+            if (!short1 && short2) {
+                return 1;
+            }
+            return new CaseInsensitiveStringComparator().compare(option1, option2);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java
index 596c8ef..f05e47b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultClassLoaderFactory.java
@@ -16,6 +16,7 @@
 
 package org.gradle.initialization;
 
+import org.apache.tools.ant.Project;
 import org.gradle.api.internal.ClassPathRegistry;
 import org.gradle.util.ClasspathUtil;
 import org.gradle.util.GFileUtils;
@@ -31,16 +32,17 @@ public class DefaultClassLoaderFactory implements ClassLoaderFactory {
     private final URLClassLoader rootClassLoader;
 
     public DefaultClassLoaderFactory(ClassPathRegistry classPathRegistry) {
-        // Add in tools.jar
-        ClassLoader parent = getClass().getClassLoader();
+        // Add in tools.jar to the Ant classloader
+        ClassLoader antClassloader = Project.class.getClassLoader();
         File toolsJar = Jvm.current().getToolsJar();
         if (toolsJar != null) {
-            ClasspathUtil.addUrl((URLClassLoader) parent, GFileUtils.toURLs(Collections.singleton(toolsJar)));
+            ClasspathUtil.addUrl((URLClassLoader) antClassloader, GFileUtils.toURLs(Collections.singleton(toolsJar)));
         }
 
         // Add in libs for plugins
+        ClassLoader runtimeClassloader = getClass().getClassLoader();
         URL[] classPath = classPathRegistry.getClassPathUrls("GRADLE_PLUGINS");
-        rootClassLoader = new URLClassLoader(classPath, parent);
+        rootClassLoader = new URLClassLoader(classPath, runtimeClassloader);
     }
 
     public ClassLoader getRootClassLoader() {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultCommandLine2StartParameterConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultCommandLine2StartParameterConverter.java
deleted file mode 100644
index 5f2773d..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultCommandLine2StartParameterConverter.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.initialization;
-
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import joptsimple.OptionException;
-import joptsimple.OptionParser;
-import joptsimple.OptionSet;
-import org.gradle.CacheUsage;
-import org.gradle.CommandLineArgumentException;
-import org.gradle.StartParameter;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
-import org.gradle.api.initialization.Settings;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.execution.DependencyReportBuildExecuter;
-import org.gradle.execution.PropertyReportBuildExecuter;
-import org.gradle.execution.TaskReportBuildExecuter;
-import org.gradle.util.WrapUtil;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultCommandLine2StartParameterConverter implements CommandLine2StartParameterConverter {
-    private static final String NO_SEARCH_UPWARDS = "u";
-    private static final String PROJECT_DIR = "p";
-    private static final String PROJECT_DEPENDENCY_TASK_NAMES = "A";
-    private static final String NO_PROJECT_DEPENDENCY_REBUILD = "a";
-    private static final String BUILD_FILE = "b";
-    public static final String INIT_SCRIPT = "I";
-    private static final String SETTINGS_FILE = "c";
-    public static final String TASKS = "t";
-    private static final String PROPERTIES = "r";
-    private static final String DEPENDENCIES = "n";
-    public static final String DEBUG = "d";
-    public static final String INFO = "i";
-    public static final String QUIET = "q";
-    public static final String FULL_STACKTRACE = "S";
-    public static final String STACKTRACE = "s";
-    private static final String SYSTEM_PROP = "D";
-    private static final String PROJECT_PROP = "P";
-    private static final String GRADLE_USER_HOME = "g";
-    private static final String EMBEDDED_SCRIPT = "e";
-    private static final String VERSION = "v";
-    private static final String CACHE = "C";
-    private static final String DRY_RUN = "m";
-    private static final String NO_OPT = "no-opt";
-    private static final String EXCLUDE_TASK = "x";
-    private static final String HELP = "h";
-    private static final String GUI = "gui";
-    private static final String ALL = "all";
-
-    private final OptionParser parser = new OptionParser() {
-        {
-            acceptsAll(WrapUtil.toList(NO_SEARCH_UPWARDS, "no-search-upward"), String.format(
-                    "Don't search in parent folders for a %s file.", Settings.DEFAULT_SETTINGS_FILE));
-            acceptsAll(WrapUtil.toList(CACHE, "cache"),
-                    "Specifies how compiled build scripts should be cached. Possible values are: 'rebuild' and 'on'. Default value is 'on'")
-                    .withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(VERSION, "version"), "Print version info.");
-            acceptsAll(WrapUtil.toList(DEBUG, "debug"), "Log in debug mode (includes normal stacktrace).");
-            acceptsAll(WrapUtil.toList(QUIET, "quiet"), "Log errors only.");
-            acceptsAll(WrapUtil.toList(DRY_RUN, "dry-run"), "Runs the builds with all task actions disabled.");
-            acceptsAll(WrapUtil.toList(INFO, "info"), "Set log level to info.");
-            acceptsAll(WrapUtil.toList(STACKTRACE, "stacktrace"),
-                    "Print out the stacktrace also for user exceptions (e.g. compile error).");
-            acceptsAll(WrapUtil.toList(FULL_STACKTRACE, "full-stacktrace"),
-                    "Print out the full (very verbose) stacktrace for any exceptions.");
-            acceptsAll(WrapUtil.toList(TASKS, "tasks"), "Show list of all available tasks.").
-                    withOptionalArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(ALL), "Show additional details in the task listing.");
-            acceptsAll(WrapUtil.toList(PROPERTIES, "properties"), "Show list of all available project properties.").
-                    withOptionalArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(DEPENDENCIES, "dependencies"), "Show list of all project dependencies.").
-                    withOptionalArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(GUI), "Launches a GUI application");
-            acceptsAll(WrapUtil.toList(PROJECT_DIR, "project-dir"),
-                    "Specifies the start directory for Gradle. Defaults to current directory.").withRequiredArg()
-                    .ofType(String.class);
-            acceptsAll(WrapUtil.toList(GRADLE_USER_HOME, "gradle-user-home"),
-                    "Specifies the gradle user home directory.").withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(INIT_SCRIPT, "init-script"), "Specifies an initialization script.")
-                    .withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(SETTINGS_FILE, "settings-file"), "Specifies the settings file.")
-                    .withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(BUILD_FILE, "build-file"), "Specifies the build file.").withRequiredArg().ofType(
-                    String.class);
-            acceptsAll(WrapUtil.toList(SYSTEM_PROP, "system-prop"),
-                    "Set system property of the JVM (e.g. -Dmyprop=myvalue).").withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(PROJECT_PROP, "project-prop"),
-                    "Set project property for the build script (e.g. -Pmyprop=myvalue).").withRequiredArg().ofType(
-                    String.class);
-            acceptsAll(WrapUtil.toList(EMBEDDED_SCRIPT, "embedded"), "Specify an embedded build script.")
-                    .withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(PROJECT_DEPENDENCY_TASK_NAMES, "dep-tasks"),
-                    "Specify additional tasks for building project dependencies.").withRequiredArg().ofType(
-                    String.class);
-            acceptsAll(WrapUtil.toList(NO_PROJECT_DEPENDENCY_REBUILD, "no-rebuild"),
-                    "Do not rebuild project dependencies.");
-            acceptsAll(WrapUtil.toList(NO_OPT), "Ignore any task optimization.");
-            acceptsAll(WrapUtil.toList(EXCLUDE_TASK, "exclude-task"), "Specify a task to be excluded from execution.")
-                    .withRequiredArg().ofType(String.class);
-            acceptsAll(WrapUtil.toList(HELP, "?", "help"), "Shows this help message");
-        }};
-
-    private static BiMap<String, LogLevel> logLevelMap = HashBiMap.create();
-    private static BiMap<String, StartParameter.ShowStacktrace> showStacktraceMap = HashBiMap.create();
-
-    //Initialize bi-directional maps so you can convert these back and forth from their command line options to their
-    //object representation.
-
-    static {
-        logLevelMap.put(QUIET, LogLevel.QUIET);
-        logLevelMap.put(INFO, LogLevel.INFO);
-        logLevelMap.put(DEBUG, LogLevel.DEBUG);
-        logLevelMap.put("", LogLevel.LIFECYCLE);
-        //there are also other log levels that gradle doesn't support command-line-wise.
-
-        showStacktraceMap.put(FULL_STACKTRACE, StartParameter.ShowStacktrace.ALWAYS_FULL);
-        showStacktraceMap.put(STACKTRACE, StartParameter.ShowStacktrace.ALWAYS);
-        //showStacktraceMap.put( , StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS ); there is no command argument for this. Rather, the lack of an argument means 'default to this'.
-    }
-
-    public StartParameter convert(String[] args) {
-        StartParameter startParameter = new StartParameter();
-        convert(args, startParameter);
-        return startParameter;
-    }
-
-    public void convert(String[] args, StartParameter startParameter) {
-        OptionSet options;
-        try {
-            options = parser.parse(args);
-        } catch (OptionException e) {
-            throw new CommandLineArgumentException(e.getMessage());
-        }
-
-        if (options.has(HELP)) {
-            startParameter.setShowHelp(true);
-            return;
-        }
-
-        if (options.has(VERSION)) {
-            startParameter.setShowVersion(true);
-            return;
-        }
-
-        if (options.has(GUI)) {
-            startParameter.setLaunchGUI(true);
-        }
-
-        if (options.has(SYSTEM_PROP)) {
-            List<String> props = (List<String>) options.valuesOf(SYSTEM_PROP);
-            for (String keyValueExpression : props) {
-                String[] elements = keyValueExpression.split("=");
-                startParameter.getSystemPropertiesArgs().put(elements[0], elements.length == 1 ? "" : elements[1]);
-            }
-        }
-
-        if (options.has(PROJECT_PROP)) {
-            List<String> props = (List<String>) options.valuesOf(PROJECT_PROP);
-            for (String keyValueExpression : props) {
-                String[] elements = keyValueExpression.split("=");
-                startParameter.getProjectProperties().put(elements[0], elements.length == 1 ? "" : elements[1]);
-            }
-        }
-
-        if (options.has(NO_SEARCH_UPWARDS)) {
-            startParameter.setSearchUpwards(false);
-        }
-
-        if (options.has(PROJECT_DIR)) {
-            startParameter.setProjectDir(new File((String) options.valueOf(PROJECT_DIR)));
-        }
-        if (options.hasArgument(GRADLE_USER_HOME)) {
-            startParameter.setGradleUserHomeDir(new File((String) options.valueOf(GRADLE_USER_HOME)));
-        }
-        if (options.hasArgument(BUILD_FILE)) {
-            startParameter.setBuildFile(new File((String) options.valueOf(BUILD_FILE)));
-        }
-        if (options.hasArgument(SETTINGS_FILE)) {
-            startParameter.setSettingsFile(new File((String) options.valueOf(SETTINGS_FILE)));
-        }
-
-        for (String script : (List<String>) options.valuesOf(INIT_SCRIPT)) {
-            startParameter.addInitScript(new File(script));
-        }
-
-        if (options.has(CACHE)) {
-            try {
-                startParameter.setCacheUsage(CacheUsage.fromString(options.valueOf(CACHE).toString()));
-            } catch (InvalidUserDataException e) {
-                throw new CommandLineArgumentException(e.getMessage());
-            }
-        }
-
-        if (options.has(EMBEDDED_SCRIPT)) {
-            if (options.has(BUILD_FILE) || options.has(NO_SEARCH_UPWARDS) || options.has(SETTINGS_FILE)) {
-                System.err.println(String.format(
-                        "Error: The -%s option can't be used together with the -%s, -%s or -%s options.",
-                        EMBEDDED_SCRIPT, BUILD_FILE, SETTINGS_FILE, NO_SEARCH_UPWARDS));
-                throw new CommandLineArgumentException(String.format(
-                        "Error: The -%s option can't be used together with the -%s, -%s or -%s options.",
-                        EMBEDDED_SCRIPT, BUILD_FILE, SETTINGS_FILE, NO_SEARCH_UPWARDS));
-            }
-            startParameter.useEmbeddedBuildFile((String) options.valueOf(EMBEDDED_SCRIPT));
-        }
-
-        if (options.has(FULL_STACKTRACE)) {
-            if (options.has(STACKTRACE)) {
-                throw new CommandLineArgumentException(String.format(
-                        "Error: The -%s option can't be used together with the -%s option.", FULL_STACKTRACE,
-                        STACKTRACE));
-            }
-            startParameter.setShowStacktrace(StartParameter.ShowStacktrace.ALWAYS_FULL);
-        } else if (options.has(STACKTRACE)) {
-            startParameter.setShowStacktrace(StartParameter.ShowStacktrace.ALWAYS);
-        }
-
-        if (options.has(TASKS) && options.has(PROPERTIES)) {
-            throw new CommandLineArgumentException(String.format(
-                    "Error: The -%s and -%s options cannot be used together.", TASKS, PROPERTIES));
-        }
-
-        if (options.has(PROJECT_DEPENDENCY_TASK_NAMES) && options.has(NO_PROJECT_DEPENDENCY_REBUILD)) {
-            throw new CommandLineArgumentException(String.format(
-                    "Error: The -%s and -%s options cannot be used together.", PROJECT_DEPENDENCY_TASK_NAMES,
-                    NO_PROJECT_DEPENDENCY_REBUILD));
-        } else if (options.has(NO_PROJECT_DEPENDENCY_REBUILD)) {
-            startParameter.setProjectDependenciesBuildInstruction(new ProjectDependenciesBuildInstruction(null));
-        } else if (options.has(PROJECT_DEPENDENCY_TASK_NAMES)) {
-            List<String> normalizedTaskNames = new ArrayList<String>();
-            for (Object o : options.valuesOf(PROJECT_DEPENDENCY_TASK_NAMES)) {
-                String taskName = (String) o;
-                normalizedTaskNames.add(taskName.trim());
-            }
-            startParameter.setProjectDependenciesBuildInstruction(new ProjectDependenciesBuildInstruction(
-                    normalizedTaskNames));
-        }
-
-        if (options.has(TASKS)) {
-            startParameter.setBuildExecuter(new TaskReportBuildExecuter((String) options.valueOf(TASKS), options.has(ALL)));
-        } else if (options.has(PROPERTIES)) {
-            startParameter.setBuildExecuter(new PropertyReportBuildExecuter((String) options.valueOf(PROPERTIES)));
-        } else if (options.has(DEPENDENCIES)) {
-            startParameter.setBuildExecuter(new DependencyReportBuildExecuter((String) options.valueOf(DEPENDENCIES)));
-        } else if (!options.nonOptionArguments().isEmpty()) {
-            startParameter.setTaskNames(options.nonOptionArguments());
-        }
-
-        if (options.has(DRY_RUN)) {
-            startParameter.setDryRun(true);
-        }
-
-        if (options.has(NO_OPT)) {
-            startParameter.setNoOpt(true);
-        }
-
-        if (options.has(EXCLUDE_TASK)) {
-            startParameter.setExcludedTaskNames((List<String>) options.valuesOf(EXCLUDE_TASK));
-        }
-
-        startParameter.setLogLevel(getLogLevel(options));
-    }
-
-    public void showHelp(OutputStream out) {
-        try {
-            parser.printHelpOn(out);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    private LogLevel getLogLevel(OptionSet options) {
-        LogLevel logLevel = null;
-        if (options.has(QUIET)) {
-            logLevel = LogLevel.QUIET;
-        }
-        if (options.has(INFO)) {
-            quitWithErrorIfLogLevelAlreadyDefined(logLevel, INFO);
-            logLevel = LogLevel.INFO;
-        }
-        if (options.has(DEBUG)) {
-            quitWithErrorIfLogLevelAlreadyDefined(logLevel, DEBUG);
-            logLevel = LogLevel.DEBUG;
-        }
-        if (logLevel == null) {
-            logLevel = LogLevel.LIFECYCLE;
-        }
-        return logLevel;
-    }
-
-    /*
-       This returns the log level object represented by the command line argument
-       @param  commandLineArgument a single command line argument (with no '-')
-       @return the corresponding log level or null if it doesn't match any.
-       @author mhunsicker
-    */
-
-    public LogLevel getLogLevel(String commandLineArgument) {
-        LogLevel logLevel = logLevelMap.get(commandLineArgument);
-        if (logLevel == null) {
-            return null;
-        }
-
-        return logLevel;
-    }
-
-    /*
-       This returns the command line argument that represents the specified
-       log level.
-       @param  logLevel       the log level.
-       @return the command line argument or null if this level cannot be
-                represented on the command line.
-       @author mhunsicker
-    */
-
-    public String getLogLevelCommandLine(LogLevel logLevel) {
-        String commandLine = logLevelMap.inverse().get(logLevel);
-        if (commandLine == null) {
-            return null;
-        }
-
-        return commandLine;
-    }
-
-    /*
-       This returns the log levels that are supported on the command line.
-       @return a collection of available log levels
-       @author mhunsicker
-    */
-
-    public Collection<LogLevel> getLogLevels() {
-        return Collections.unmodifiableCollection(logLevelMap.values());
-    }
-
-    /*
-       This returns the stack trace level object represented by the command
-       line argument
-       @param  commandLineArgument a single command line argument (with no '-')
-       @return the corresponding stack trace level or null if it doesn't match any.
-       @author mhunsicker
-    */
-
-    public StartParameter.ShowStacktrace getShowStacktrace(String commandLineArgument) {
-        StartParameter.ShowStacktrace showStacktrace = showStacktraceMap.get(commandLineArgument);
-        if (showStacktrace == null) {
-            return null;
-        }
-
-        return showStacktrace;
-    }
-
-    /*
-       This returns the command line argument that represents the specified
-       stack trace level.
-
-       @param  showStacktrace the stack trace level.
-       @return the command line argument or null if this level cannot be
-                represented on the command line.
-       @author mhunsicker
-    */
-
-    public String getShowStacktraceCommandLine(StartParameter.ShowStacktrace showStacktrace) {
-        String commandLine = showStacktraceMap.inverse().get(showStacktrace);
-        if (commandLine == null) {
-            return null;
-        }
-
-        return commandLine;
-    }
-
-    /*
-       This returns the ShowStacktrace levels that are supported on the command
-       line.
-       @return a collection of available ShowStacktrace levels
-       @author mhunsicker
-    */
-
-    public Collection<StartParameter.ShowStacktrace> getShowStacktrace() {
-        return Collections.unmodifiableCollection(showStacktraceMap.values());
-    }
-
-    private void quitWithErrorIfLogLevelAlreadyDefined(LogLevel logLevel, String option) {
-        if (logLevel != null) {
-            System.err.println(String.format(
-                    "Error: The log level is already defined by another option. Therefore the option %s is invalid.",
-                    option));
-            throw new InvalidUserDataException();
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
new file mode 100644
index 0000000..7cd567e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultCommandLineConverter.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import org.gradle.CacheUsage;
+import org.gradle.CommandLineArgumentException;
+import org.gradle.StartParameter;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.initialization.Settings;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.configuration.ImplicitTasksConfigurer;
+import org.gradle.logging.LoggingConfiguration;
+import org.gradle.logging.internal.LoggingCommandLineConverter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultCommandLineConverter extends AbstractCommandLineConverter<StartParameter> {
+    private static final String NO_SEARCH_UPWARDS = "u";
+    private static final String PROJECT_DIR = "p";
+    private static final String PROJECT_DEPENDENCY_TASK_NAMES = "A";
+    private static final String NO_PROJECT_DEPENDENCY_REBUILD = "a";
+    private static final String BUILD_FILE = "b";
+    public static final String INIT_SCRIPT = "I";
+    private static final String SETTINGS_FILE = "c";
+    private static final String TASKS = "t";
+    private static final String PROPERTIES = "r";
+    private static final String DEPENDENCIES = "n";
+    public static final String FULL_STACKTRACE = "S";
+    public static final String STACKTRACE = "s";
+    private static final String SYSTEM_PROP = "D";
+    private static final String PROJECT_PROP = "P";
+    private static final String GRADLE_USER_HOME = "g";
+    private static final String EMBEDDED_SCRIPT = "e";
+    private static final String CACHE = "C";
+    private static final String DRY_RUN = "m";
+    private static final String NO_OPT = "no-opt";
+    private static final String EXCLUDE_TASK = "x";
+    private static final String PROFILE = "profile";
+
+    private static BiMap<String, StartParameter.ShowStacktrace> showStacktraceMap = HashBiMap.create();
+    private final CommandLineConverter<LoggingConfiguration> loggingConfigurationCommandLineConverter = new LoggingCommandLineConverter();
+
+    //Initialize bi-directional maps so you can convert these back and forth from their command line options to their
+    //object representation.
+
+    static {
+        showStacktraceMap.put(FULL_STACKTRACE, StartParameter.ShowStacktrace.ALWAYS_FULL);
+        showStacktraceMap.put(STACKTRACE, StartParameter.ShowStacktrace.ALWAYS);
+        //showStacktraceMap.put( , StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS ); there is no command argument for this. Rather, the lack of an argument means 'default to this'.
+    }
+
+    public void configure(CommandLineParser parser) {
+        loggingConfigurationCommandLineConverter.configure(parser);
+        parser.allowMixedSubcommandsAndOptions();
+        parser.option(NO_SEARCH_UPWARDS, "no-search-upward").hasDescription(String.format("Don't search in parent folders for a %s file.", Settings.DEFAULT_SETTINGS_FILE));
+        parser.option(CACHE, "cache").hasArgument().hasDescription("Specifies how compiled build scripts should be cached. Possible values are: 'rebuild' and 'on'. Default value is 'on'");
+        parser.option(DRY_RUN, "dry-run").hasDescription("Runs the builds with all task actions disabled.");
+        parser.option(STACKTRACE, "stacktrace").hasDescription("Print out the stacktrace also for user exceptions (e.g. compile error).");
+        parser.option(FULL_STACKTRACE, "full-stacktrace").hasDescription("Print out the full (very verbose) stacktrace for any exceptions.");
+        parser.option(TASKS, "tasks").mapsToSubcommand(ImplicitTasksConfigurer.TASKS_TASK).hasDescription("Show list of available tasks [deprecated - use 'gradle tasks' instead].");
+        parser.option(PROPERTIES, "properties").mapsToSubcommand(ImplicitTasksConfigurer.PROPERTIES_TASK).hasDescription("Show list of all available project properties [deprecated - use 'gradle properties' instead].");
+        parser.option(DEPENDENCIES, "dependencies").mapsToSubcommand(ImplicitTasksConfigurer.DEPENDENCIES_TASK).hasDescription("Show list of all project dependencies [deprecated - use 'gradle dependencies' instead].");
+        parser.option(PROJECT_DIR, "project-dir").hasArgument().hasDescription("Specifies the start directory for Gradle. Defaults to current directory.");
+        parser.option(GRADLE_USER_HOME, "gradle-user-home").hasArgument().hasDescription("Specifies the gradle user home directory.");
+        parser.option(INIT_SCRIPT, "init-script").hasArguments().hasDescription("Specifies an initialization script.");
+        parser.option(SETTINGS_FILE, "settings-file").hasArgument().hasDescription("Specifies the settings file.");
+        parser.option(BUILD_FILE, "build-file").hasArgument().hasDescription("Specifies the build file.");
+        parser.option(SYSTEM_PROP, "system-prop").hasArguments().hasDescription("Set system property of the JVM (e.g. -Dmyprop=myvalue).");
+        parser.option(PROJECT_PROP, "project-prop").hasArguments().hasDescription("Set project property for the build script (e.g. -Pmyprop=myvalue).");
+        parser.option(EMBEDDED_SCRIPT, "embedded").hasArgument().hasDescription("Specify an embedded build script.");
+        parser.option(PROJECT_DEPENDENCY_TASK_NAMES, "dep-tasks").hasArguments().hasDescription("Specify additional tasks for building project dependencies.");
+        parser.option(NO_PROJECT_DEPENDENCY_REBUILD, "no-rebuild").hasDescription("Do not rebuild project dependencies.");
+        parser.option(NO_OPT).hasDescription("Ignore any task optimization.");
+        parser.option(EXCLUDE_TASK, "exclude-task").hasArguments().hasDescription("Specify a task to be excluded from execution.");
+        parser.option(PROFILE).hasDescription("Profiles build execution time and generates a report in the <build_dir>/reports/profile directory.");
+    }
+
+    public StartParameter convert(ParsedCommandLine args) throws CommandLineArgumentException {
+        return convert(args, new StartParameter());
+    }
+
+    public StartParameter convert(ParsedCommandLine options, StartParameter startParameter) throws CommandLineArgumentException {
+        LoggingConfiguration loggingConfiguration = loggingConfigurationCommandLineConverter.convert(options);
+        startParameter.setLogLevel(loggingConfiguration.getLogLevel());
+        startParameter.setColorOutput(loggingConfiguration.isColorOutput());
+
+        for (String keyValueExpression : options.option(SYSTEM_PROP).getValues()) {
+            String[] elements = keyValueExpression.split("=");
+            startParameter.getSystemPropertiesArgs().put(elements[0], elements.length == 1 ? "" : elements[1]);
+        }
+
+        for (String keyValueExpression : options.option(PROJECT_PROP).getValues()) {
+            String[] elements = keyValueExpression.split("=");
+            startParameter.getProjectProperties().put(elements[0], elements.length == 1 ? "" : elements[1]);
+        }
+
+        if (options.hasOption(NO_SEARCH_UPWARDS)) {
+            startParameter.setSearchUpwards(false);
+        }
+
+        if (options.hasOption(PROJECT_DIR)) {
+            startParameter.setProjectDir(new File(options.option(PROJECT_DIR).getValue()));
+        }
+        if (options.hasOption(GRADLE_USER_HOME)) {
+            startParameter.setGradleUserHomeDir(new File(options.option(GRADLE_USER_HOME).getValue()));
+        }
+        if (options.hasOption(BUILD_FILE)) {
+            startParameter.setBuildFile(new File(options.option(BUILD_FILE).getValue()));
+        }
+        if (options.hasOption(SETTINGS_FILE)) {
+            startParameter.setSettingsFile(new File(options.option(SETTINGS_FILE).getValue()));
+        }
+
+        for (String script : options.option(INIT_SCRIPT).getValues()) {
+            startParameter.addInitScript(new File(script));
+        }
+
+        if (options.hasOption(CACHE)) {
+            try {
+                startParameter.setCacheUsage(CacheUsage.fromString(options.option(CACHE).getValue()));
+            } catch (InvalidUserDataException e) {
+                throw new CommandLineArgumentException(e.getMessage());
+            }
+        }
+
+        if (options.hasOption(EMBEDDED_SCRIPT)) {
+            if (options.hasOption(BUILD_FILE) || options.hasOption(NO_SEARCH_UPWARDS) || options.hasOption(SETTINGS_FILE)) {
+                System.err.println(String.format(
+                        "Error: The -%s option can't be used together with the -%s, -%s or -%s options.",
+                        EMBEDDED_SCRIPT, BUILD_FILE, SETTINGS_FILE, NO_SEARCH_UPWARDS));
+                throw new CommandLineArgumentException(String.format(
+                        "Error: The -%s option can't be used together with the -%s, -%s or -%s options.",
+                        EMBEDDED_SCRIPT, BUILD_FILE, SETTINGS_FILE, NO_SEARCH_UPWARDS));
+            }
+            startParameter.useEmbeddedBuildFile(options.option(EMBEDDED_SCRIPT).getValue());
+        }
+
+        if (options.hasOption(FULL_STACKTRACE)) {
+            if (options.hasOption(STACKTRACE)) {
+                throw new CommandLineArgumentException(String.format(
+                        "Error: The -%s option can't be used together with the -%s option.", FULL_STACKTRACE,
+                        STACKTRACE));
+            }
+            startParameter.setShowStacktrace(StartParameter.ShowStacktrace.ALWAYS_FULL);
+        } else if (options.hasOption(STACKTRACE)) {
+            startParameter.setShowStacktrace(StartParameter.ShowStacktrace.ALWAYS);
+        }
+
+        if (options.hasOption(PROJECT_DEPENDENCY_TASK_NAMES) && options.hasOption(NO_PROJECT_DEPENDENCY_REBUILD)) {
+            throw new CommandLineArgumentException(String.format(
+                    "Error: The -%s and -%s options cannot be used together.", PROJECT_DEPENDENCY_TASK_NAMES,
+                    NO_PROJECT_DEPENDENCY_REBUILD));
+        } else if (options.hasOption(NO_PROJECT_DEPENDENCY_REBUILD)) {
+            startParameter.setProjectDependenciesBuildInstruction(new ProjectDependenciesBuildInstruction(null));
+        } else if (options.hasOption(PROJECT_DEPENDENCY_TASK_NAMES)) {
+            List<String> normalizedTaskNames = new ArrayList<String>();
+            for (String taskName : options.option(PROJECT_DEPENDENCY_TASK_NAMES).getValues()) {
+                normalizedTaskNames.add(taskName);
+            }
+            startParameter.setProjectDependenciesBuildInstruction(new ProjectDependenciesBuildInstruction(
+                    normalizedTaskNames));
+        }
+
+        if (!options.getExtraArguments().isEmpty()) {
+            startParameter.setTaskNames(options.getExtraArguments());
+        }
+
+        if (options.hasOption(DRY_RUN)) {
+            startParameter.setDryRun(true);
+        }
+
+        if (options.hasOption(NO_OPT)) {
+            startParameter.setNoOpt(true);
+        }
+
+        if (options.hasOption(EXCLUDE_TASK)) {
+            startParameter.setExcludedTaskNames(options.option(EXCLUDE_TASK).getValues());
+        }
+
+        if (options.hasOption(PROFILE)) {
+            startParameter.setProfile(true);
+        }
+
+        return startParameter;
+    }
+
+    /**
+     * This returns the stack trace level object represented by the command line argument
+     *
+     * @param commandLineArgument a single command line argument (with no '-')
+     * @return the corresponding stack trace level or null if it doesn't match any.
+     * @author mhunsicker
+     */
+    public StartParameter.ShowStacktrace getShowStacktrace(String commandLineArgument) {
+        StartParameter.ShowStacktrace showStacktrace = showStacktraceMap.get(commandLineArgument);
+        if (showStacktrace == null) {
+            return null;
+        }
+
+        return showStacktrace;
+    }
+
+    /**
+     * This returns the command line argument that represents the specified stack trace level.
+     *
+     * @param showStacktrace the stack trace level.
+     * @return the command line argument or null if this level cannot be represented on the command line.
+     * @author mhunsicker
+     */
+    public String getShowStacktraceCommandLine(StartParameter.ShowStacktrace showStacktrace) {
+        String commandLine = showStacktraceMap.inverse().get(showStacktrace);
+        if (commandLine == null) {
+            return null;
+        }
+
+        return commandLine;
+    }
+
+    /**
+     * This returns the ShowStacktrace levels that are supported on the command line.
+     *
+     * @return a collection of available ShowStacktrace levels
+     * @author mhunsicker
+     */
+    public Collection<StartParameter.ShowStacktrace> getShowStacktrace() {
+        return Collections.unmodifiableCollection(showStacktraceMap.values());
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
index 12db113..9b06b13 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncher.java
@@ -133,7 +133,7 @@ public class DefaultGradleLauncher extends GradleLauncher {
         buildListener.projectsLoaded(gradle);
 
         // Configure build
-        buildConfigurer.process(gradle.getRootProject());
+        buildConfigurer.configure(gradle);
         buildListener.projectsEvaluated(gradle);
 
         if (upTo == Stage.Configure) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
index 4a4bb23..01a7c2a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultGradleLauncherFactory.java
@@ -23,63 +23,84 @@ import org.gradle.api.internal.project.IProjectFactory;
 import org.gradle.api.internal.project.ServiceRegistry;
 import org.gradle.api.internal.project.TopLevelBuildServiceRegistry;
 import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logging;
 import org.gradle.api.logging.StandardOutputListener;
 import org.gradle.cache.CacheRepository;
 import org.gradle.configuration.BuildConfigurer;
-import org.gradle.configuration.ProjectDependencies2TaskResolver;
 import org.gradle.invocation.DefaultGradle;
 import org.gradle.listener.ListenerManager;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
-import org.gradle.logging.ProgressLoggingBridge;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.profile.ProfileListener;
+import org.gradle.util.Clock;
 import org.gradle.util.WrapUtil;
 
+import java.util.Arrays;
+
 /**
  * @author Hans Dockter
  */
 public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
     private final ServiceRegistry sharedServices;
     private final NestedBuildTracker tracker;
-    private CommandLine2StartParameterConverter commandLine2StartParameterConverter;
+    private CommandLineConverter<StartParameter> commandLineConverter;
 
+    public DefaultGradleLauncherFactory(ServiceRegistry loggingServices) {
+        this(new GlobalServicesRegistry(loggingServices));
+    }
+    
     public DefaultGradleLauncherFactory() {
-        sharedServices = new GlobalServicesRegistry();
+        this(new GlobalServicesRegistry());
+    }
+
+    private DefaultGradleLauncherFactory(GlobalServicesRegistry globalServices) {
+        sharedServices = globalServices;
 
         // Start logging system
-        sharedServices.get(LoggingManagerFactory.class).create().setLevel(LogLevel.LIFECYCLE).start();
+        sharedServices.newInstance(LoggingManagerInternal.class).setLevel(LogLevel.LIFECYCLE).start();
 
-        commandLine2StartParameterConverter = sharedServices.get(CommandLine2StartParameterConverter.class);
+        commandLineConverter = sharedServices.get(CommandLineConverter.class);
         tracker = new NestedBuildTracker();
 
         // Register default loggers 
         ListenerManager listenerManager = sharedServices.get(ListenerManager.class);
-        listenerManager.useLogger(new ProgressLoggingBridge());
         listenerManager.addListener(new BuildProgressLogger(sharedServices.get(ProgressLoggerFactory.class)));
+
+        GradleLauncher.injectCustomFactory(this);
     }
 
-    public StartParameter createStartParameter(String[] commandLineArgs) {
-        return commandLine2StartParameterConverter.convert(commandLineArgs);
+    public StartParameter createStartParameter(String... commandLineArgs) {
+        return commandLineConverter.convert(Arrays.asList(commandLineArgs));
     }
 
-    public GradleLauncher newInstance(String[] commandLineArgs) {
-        return newInstance(commandLine2StartParameterConverter.convert(commandLineArgs));
+    public GradleLauncher newInstance(String... commandLineArgs) {
+        return newInstance(createStartParameter(commandLineArgs));
     }
 
     public GradleLauncher newInstance(StartParameter startParameter) {
+        Clock buildClock = new Clock();
         TopLevelBuildServiceRegistry serviceRegistry = new TopLevelBuildServiceRegistry(sharedServices, startParameter);
         ListenerManager listenerManager = serviceRegistry.get(ListenerManager.class);
-        LoggingManagerInternal loggingManager = serviceRegistry.get(LoggingManagerFactory.class).create();
+        LoggingManagerInternal loggingManager = serviceRegistry.newInstance(LoggingManagerInternal.class);
         loggingManager.setLevel(startParameter.getLogLevel());
+        loggingManager.colorStdOutAndStdErr(startParameter.isColorOutput());
 
         //this hooks up the ListenerManager and LoggingConfigurer so you can call Gradle.addListener() with a StandardOutputListener.
         loggingManager.addStandardOutputListener(listenerManager.getBroadcaster(StandardOutputListener.class));
         loggingManager.addStandardErrorListener(listenerManager.getBroadcaster(StandardOutputListener.class));
 
         listenerManager.useLogger(new TaskExecutionLogger(serviceRegistry.get(ProgressLoggerFactory.class)));
+        if (tracker.getCurrentBuild() == null) {
+            listenerManager.useLogger(new BuildLogger(Logging.getLogger(BuildLogger.class), serviceRegistry.get(StyledTextOutputFactory.class), buildClock, startParameter));
+        }
         listenerManager.addListener(tracker);
         listenerManager.addListener(new BuildCleanupListener(serviceRegistry));
 
+        if (startParameter.isProfile()) {
+            listenerManager.addListener(new ProfileListener(buildClock.getTimeInMs()));
+        }
+
         DefaultGradle gradle = new DefaultGradle(
                 tracker.getCurrentBuild(),
                 startParameter, serviceRegistry);
@@ -100,14 +121,15 @@ public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
                 new BuildLoader(
                         serviceRegistry.get(IProjectFactory.class)
                 ),
-                new BuildConfigurer(new ProjectDependencies2TaskResolver()), gradle.getBuildListenerBroadcaster(),
+                serviceRegistry.get(BuildConfigurer.class),
+                gradle.getBuildListenerBroadcaster(),
                 serviceRegistry.get(ExceptionAnalyser.class),
                 loggingManager);
     }
 
-    public void setCommandLine2StartParameterConverter(
-            CommandLine2StartParameterConverter commandLine2StartParameterConverter) {
-        this.commandLine2StartParameterConverter = commandLine2StartParameterConverter;
+    public void setCommandLineConverter(
+            CommandLineConverter<StartParameter> commandLineConverter) {
+        this.commandLineConverter = commandLineConverter;
     }
 
     private static class BuildCleanupListener extends BuildAdapter {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java
index 5edc668..1a33d47 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptor.java
@@ -15,15 +15,15 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.Project;
+import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.project.ProjectIdentifier;
-import org.gradle.util.PathHelper;
 import org.gradle.util.GFileUtils;
+import org.gradle.util.Path;
 
 import java.io.File;
-import java.util.Set;
 import java.util.LinkedHashSet;
+import java.util.Set;
 
 /**
  * @author Hans Dockter
@@ -34,7 +34,7 @@ public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdent
     private DefaultProjectDescriptor parent;
     private Set<ProjectDescriptor> children = new LinkedHashSet<ProjectDescriptor>();
     private IProjectDescriptorRegistry projectDescriptorRegistry;
-    private String path;
+    private Path path;
     private String buildFileName = Project.DEFAULT_BUILD_FILE;
 
     public DefaultProjectDescriptor(DefaultProjectDescriptor parent, String name, File dir,
@@ -50,20 +50,16 @@ public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdent
         }
     }
 
-    private String path(String name) {
+    private Path path(String name) {
         if (isRootDescriptor()) {
-            return path = Project.PATH_SEPARATOR;
+            return path = Path.ROOT;
         } else {
             return parent.absolutePath(name);
         }
     }
 
-    private String absolutePath(String path) {
-        if (!PathHelper.isAbsolutePath(path)) {
-            String prefix = isRootDescriptor() ? "" : Project.PATH_SEPARATOR;
-            return this.path + prefix + path;
-        }
-        return path;
+    private Path absolutePath(String path) {
+        return this.path.resolve(path);
     }
 
     private boolean isRootDescriptor() {
@@ -75,7 +71,7 @@ public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdent
     }
 
     public void setName(String name) {
-        projectDescriptorRegistry.changeDescriptorPath(getPath(), path(name));
+        projectDescriptorRegistry.changeDescriptorPath(path, path(name));
         this.name = name;
     }
 
@@ -100,10 +96,10 @@ public class DefaultProjectDescriptor implements ProjectDescriptor, ProjectIdent
     }
 
     public String getPath() {
-        return path;
+        return path.toString();
     }
 
-    void setPath(String path) {
+    void setPath(Path path) {
         this.path = path;
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java
index 3f54ae8..148e100 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistry.java
@@ -16,14 +16,15 @@
 package org.gradle.initialization;
 
 import org.gradle.api.internal.project.DefaultProjectRegistry;
+import org.gradle.util.Path;
 
 /**
  * @author Hans Dockter
  */
 public class DefaultProjectDescriptorRegistry extends DefaultProjectRegistry<DefaultProjectDescriptor> implements IProjectDescriptorRegistry {
 
-    public void changeDescriptorPath(String oldPath, String newPath) {
-        DefaultProjectDescriptor projectDescriptor = removeProject(oldPath);
+    public void changeDescriptorPath(Path oldPath, Path newPath) {
+        DefaultProjectDescriptor projectDescriptor = removeProject(oldPath.toString());
         projectDescriptor.setPath(newPath);
         addProject(projectDescriptor);
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java
index 1ee52f4..8dde639 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/IProjectDescriptorRegistry.java
@@ -16,10 +16,11 @@
 package org.gradle.initialization;
 
 import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.util.Path;
 
 /**
  * @author Hans Dockter
  */
 public interface IProjectDescriptorRegistry extends IProjectRegistry<DefaultProjectDescriptor> {
-    void changeDescriptorPath(String oldPath, String newPath);
+    void changeDescriptorPath(Path oldPath, Path newPath);
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ParsedCommandLine.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ParsedCommandLine.java
new file mode 100644
index 0000000..94c192b
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ParsedCommandLine.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import org.gradle.util.GUtil;
+
+import java.util.*;
+
+public class ParsedCommandLine {
+    private final Map<String, ParsedCommandLineOption> optionsByString = new HashMap<String, ParsedCommandLineOption>();
+    private final Set<String> presentOptions = new HashSet<String>();
+    private final List<String> extraArguments = new ArrayList<String>();
+
+    ParsedCommandLine(Iterable<CommandLineOption> options) {
+        for (CommandLineOption option : options) {
+            ParsedCommandLineOption parsedOption = new ParsedCommandLineOption(option);
+            for (String optionStr : option.getOptions()) {
+                optionsByString.put(optionStr, parsedOption);
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("options: %s, extraArguments: %s", GUtil.toString(presentOptions), GUtil.toString(extraArguments));
+    }
+
+    public boolean hasOption(String option) {
+        option(option);
+        return presentOptions.contains(option);
+    }
+    
+    public ParsedCommandLineOption option(String option) {
+        ParsedCommandLineOption parsedOption = optionsByString.get(option);
+        if (parsedOption == null) {
+            throw new IllegalArgumentException(String.format("Option '%s' not defined.", option));
+        }
+        return parsedOption;
+    }
+    
+    public List<String> getExtraArguments() {
+        return extraArguments;
+    }
+
+    void addExtraValue(String value) {
+        extraArguments.add(value);
+    }
+
+    ParsedCommandLineOption addOption(String option) {
+        ParsedCommandLineOption parsedOption = optionsByString.get(option);
+        if (parsedOption == null) {
+            return null;
+        }
+        presentOptions.addAll(parsedOption.getOption().getOptions());
+        return parsedOption;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ParsedCommandLineOption.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ParsedCommandLineOption.java
new file mode 100644
index 0000000..e68b3c8
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ParsedCommandLineOption.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ParsedCommandLineOption {
+    private final List<String> values = new ArrayList<String>();
+    private final CommandLineOption option;
+
+    ParsedCommandLineOption(CommandLineOption option) {
+        this.option = option;
+    }
+
+    public CommandLineOption getOption() {
+        return option;
+    }
+
+    public String getValue() {
+        if (values.isEmpty()) {
+            throw new IllegalStateException("Option does not have any value.");
+        }
+        if (values.size() > 1) {
+            throw new IllegalStateException("Option has multiple values.");
+        }
+        return values.get(0);
+    }
+
+    public List<String> getValues() {
+        return values;
+    }
+
+    public void addArgument(String argument) {
+        values.add(argument);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
index c3a7f42..e8de24b 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/initialization/ScriptEvaluatingSettingsProcessor.java
@@ -38,10 +38,6 @@ public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
 
     private ScriptPluginFactory configurerFactory;
 
-    public ScriptEvaluatingSettingsProcessor() {
-
-    }
-
     public ScriptEvaluatingSettingsProcessor(ScriptPluginFactory configurerFactory,
                                              SettingsFactory settingsFactory) {
         this.configurerFactory = configurerFactory;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/invocation/DefaultGradle.java b/subprojects/gradle-core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
index 9a9fee1..191c86f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/invocation/DefaultGradle.java
@@ -57,6 +57,11 @@ public class DefaultGradle implements GradleInternal {
         distributionLocator = services.get(GradleDistributionLocator.class);
     }
 
+    @Override
+    public String toString() {
+        return rootProject == null ? "build" : String.format("build '%s'", rootProject.getName());
+    }
+
     public Gradle getParent() {
         return parent;
     }
@@ -155,7 +160,7 @@ public class DefaultGradle implements GradleInternal {
         return this;
     }
 
-    public ServiceRegistryFactory getServiceRegistryFactory() {
+    public ServiceRegistryFactory getServices() {
         return services;
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/listener/ListenerManager.java b/subprojects/gradle-core/src/main/groovy/org/gradle/listener/ListenerManager.java
index bcb20d9..d3f9990 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/listener/ListenerManager.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/listener/ListenerManager.java
@@ -22,7 +22,7 @@ import groovy.lang.Closure;
  * Unified manager for all listeners for Gradle.  Provides a simple way to find all listeners of a given type in the
  * system.
  *
- * While the methods work with any Object, in general only interfaces should be used as listeners.  Also, due to
+ * While the methods work with any Object, in general only interfaces should be used as listener types.  Also, due to
  * implementation details, any listener method with a non-void return type will return a null.
  */
 public interface ListenerManager {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/AbstractProgressLoggingAwareFormatter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/AbstractProgressLoggingAwareFormatter.java
deleted file mode 100644
index 9183ffc..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/AbstractProgressLoggingAwareFormatter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.PatternLayout;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.Context;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.logging.Logging;
-
-import java.io.IOException;
-import java.util.LinkedList;
-
-public abstract class AbstractProgressLoggingAwareFormatter implements LogEventFormatter {
-    public static final String EOL = System.getProperty("line.separator");
-    private final PatternLayout layout;
-    private final LinkedList<Operation> pendingOperations = new LinkedList<Operation>();
-
-    protected AbstractProgressLoggingAwareFormatter(Context context) {
-        this.layout = new PatternLayout();
-        layout.setContext(context);
-        layout.setPattern("%msg%n%ex");
-        layout.start();
-    }
-
-    public void format(ILoggingEvent event) {
-        try {
-            if (event.getMarker() == Logging.PROGRESS_STARTED) {
-                Operation operation = new Operation();
-                operation.description = event.getFormattedMessage();
-                operation.status = "";
-                pendingOperations.addFirst(operation);
-                onStart(operation);
-            } else if (event.getMarker() == Logging.PROGRESS) {
-                assert !pendingOperations.isEmpty();
-                Operation operation = pendingOperations.getFirst();
-                operation.status = event.getFormattedMessage();
-                onStatusChange(operation);
-            } else if (event.getMarker() == Logging.PROGRESS_COMPLETE) {
-                assert !pendingOperations.isEmpty();
-                Operation operation = pendingOperations.removeFirst();
-                operation.status = event.getFormattedMessage();
-                onComplete(operation);
-            } else if (event.getLevel() == Level.ERROR) {
-                String message = layout.doLayout(event);
-                onErrorMessage(message);
-            } else {
-                String message = layout.doLayout(event);
-                onInfoMessage(message);
-            }
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    protected abstract void onStart(Operation operation) throws IOException;
-
-    protected abstract void onStatusChange(Operation operation) throws IOException;
-
-    protected abstract void onComplete(Operation operation) throws IOException;
-
-    protected abstract void onInfoMessage(String message) throws IOException;
-
-    protected abstract void onErrorMessage(String message) throws IOException;
-
-    protected class Operation {
-        private String description;
-        private String status;
-
-        public String getDescription() {
-            return description;
-        }
-
-        public String getStatus() {
-            return status;
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/AnsiConsole.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/AnsiConsole.java
deleted file mode 100644
index d15e1de..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/AnsiConsole.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.apache.commons.lang.StringUtils;
-import org.fusesource.jansi.Ansi;
-import org.gradle.api.Action;
-import org.gradle.api.UncheckedIOException;
-
-import java.io.Flushable;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-public class AnsiConsole implements Console {
-    private final static String EOL = System.getProperty("line.separator");
-    private final Appendable target;
-    private final Flushable flushable;
-    private final LinkedList<LabelImpl> statusBars = new LinkedList<LabelImpl>();
-    private final TextAreaImpl textArea;
-    private Widget bottomWidget;
-    private final Screen container;
-
-    public AnsiConsole(Appendable target, Flushable flushable) {
-        this.target = target;
-        this.flushable = flushable;
-        container = new Screen();
-        textArea = new TextAreaImpl(container);
-        bottomWidget = textArea;
-    }
-
-    public Label addStatusBar() {
-        final LabelImpl statusBar = new LabelImpl(container);
-        render(new Action<Ansi>() {
-            public void execute(Ansi ansi) {
-                bottomWidget.removeFromLastLine(ansi);
-                statusBar.draw(ansi);
-            }
-        });
-        statusBars.addFirst(statusBar);
-        bottomWidget = statusBar;
-        return statusBar;
-    }
-
-    public TextArea getMainArea() {
-        return textArea;
-    }
-
-    private void render(Action<Ansi> action) {
-        Ansi ansi = createAnsi();
-        action.execute(ansi);
-        try {
-            target.append(ansi.toString());
-            flushable.flush();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    Ansi createAnsi() {
-        return Ansi.ansi();
-    }
-
-    private interface Container {
-        void redraw(Widget widget, Action<Ansi> drawOperation);
-
-        void close(Widget widget);
-    }
-
-    private interface Widget {
-        /**
-         * Removes content of this widget from the last line of the screen. Leaves cursor at left edge of bottom-most
-         * line.
-         */
-        void removeFromLastLine(Ansi ansi);
-    }
-
-    private class Screen implements Container {
-        public void redraw(Widget widget, final Action<Ansi> drawOperation) {
-            final LabelImpl currentStatusBar = statusBars.peek();
-            if (widget == textArea) {
-                render(new Action<Ansi>() {
-                    public void execute(Ansi ansi) {
-                        if (currentStatusBar != null) {
-                            currentStatusBar.removeFromLastLine(ansi);
-                        }
-                        drawOperation.execute(ansi);
-                        if (currentStatusBar != null) {
-                            textArea.removeFromLastLine(ansi);
-                            currentStatusBar.draw(ansi);
-                        }
-                    }
-                });
-            } else {
-                final LabelImpl statusBar = (LabelImpl) widget;
-                if (statusBar != currentStatusBar) {
-                    return;
-                }
-                render(new Action<Ansi>() {
-                    public void execute(Ansi ansi) {
-                        drawOperation.execute(ansi);
-                    }
-                });
-            }
-        }
-
-        public void close(Widget widget) {
-            if (widget == textArea) {
-                throw new UnsupportedOperationException();
-            }
-            final LabelImpl statusBar = (LabelImpl) widget;
-            statusBars.remove(statusBar);
-            if (statusBar == bottomWidget) {
-                render(new Action<Ansi>() {
-                    public void execute(Ansi ansi) {
-                        statusBar.removeFromLastLine(ansi);
-                        LabelImpl current = statusBars.peek();
-                        if (current != null) {
-                            current.draw(ansi);
-                            bottomWidget = current;
-                        } else {
-                            bottomWidget = textArea;
-                        }
-                    }
-                });
-            }
-        }
-    }
-
-    private class LabelImpl implements Label, Widget {
-        private final Container container;
-        private String text = "";
-        private String displayedText = "";
-
-        public LabelImpl(Container container) {
-            this.container = container;
-        }
-
-        public void setText(String text) {
-            if (text.equals(this.text)) {
-                return;
-            }
-            this.text = text;
-            container.redraw(this, new Action<Ansi>() {
-                public void execute(Ansi ansi) {
-                    draw(ansi);
-                }
-            });
-        }
-
-        public void close() {
-            container.close(this);
-        }
-
-        public void removeFromLastLine(Ansi ansi) {
-            if (displayedText.length() > 0) {
-                ansi.cursorLeft(displayedText.length());
-                ansi.eraseLine(Ansi.Erase.FORWARD);
-                displayedText = "";
-            }
-        }
-
-        public void draw(Ansi ansi) {
-            String prefix = StringUtils.getCommonPrefix(new String[]{text, displayedText});
-            if (prefix.length() < displayedText.length()) {
-                ansi.cursorLeft(displayedText.length() - prefix.length());
-            }
-            if (prefix.length() < text.length()) {
-                ansi.a(text.substring(prefix.length()));
-            }
-            if (displayedText.length() > text.length()) {
-                ansi.eraseLine(Ansi.Erase.FORWARD);
-            }
-            displayedText = text;
-        }
-    }
-
-    private class TextAreaImpl implements TextArea, Widget {
-        private final Container container;
-        private int width;
-        boolean extraEol;
-
-        private TextAreaImpl(Container container) {
-            this.container = container;
-        }
-
-        public void removeFromLastLine(Ansi ansi) {
-            if (width > 0) {
-                ansi.newline();
-                extraEol = true;
-            }
-        }
-
-        public void append(final CharSequence text) {
-            if (text.length() == 0) {
-                return;
-            }
-            container.redraw(this, new Action<Ansi>() {
-                public void execute(Ansi ansi) {
-                    if (extraEol) {
-                        ansi.cursorUp(1);
-                        ansi.cursorRight(width);
-                        extraEol = false;
-                    }
-
-                    Iterator<String> tokenizer = new LineSplitter(text);
-                    while (tokenizer.hasNext()) {
-                        String token = tokenizer.next();
-                        if (token.equals(EOL)) {
-                            width = 0;
-                            extraEol = false;
-                        } else {
-                            width += token.length();
-                        }
-                        ansi.a(token);
-                    }
-                }
-            });
-        }
-    }
-
-    private static class LineSplitter implements Iterator<String> {
-        private final CharSequence text;
-        private int start;
-        private int end;
-
-        private LineSplitter(CharSequence text) {
-            this.text = text;
-            findNext();
-        }
-
-        public boolean findNext() {
-            if (end == text.length()) {
-                start = -1;
-                return false;
-            }
-            if (startsWithEol(text, end)) {
-                start = end;
-                end = start + EOL.length();
-                return true;
-            }
-            int pos = end;
-            while (pos < text.length()) {
-                if (startsWithEol(text, pos)) {
-                    start = end;
-                    end = pos;
-                    return true;
-                }
-                pos++;
-            }
-            start = end;
-            end = text.length();
-            return true;
-        }
-
-        private boolean startsWithEol(CharSequence text, int startAt) {
-            if (startAt + EOL.length() > text.length()) {
-                return false;
-            }
-            for (int i = 0; i < EOL.length(); i++) {
-                if (EOL.charAt(i) != text.charAt(startAt + i)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        public boolean hasNext() {
-            return start >= 0;
-        }
-
-        public String next() {
-            CharSequence next = text.subSequence(start, end);
-            findNext();
-            return next.toString();
-        }
-
-        public void remove() {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/BasicProgressLoggingAwareFormatter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/BasicProgressLoggingAwareFormatter.java
deleted file mode 100644
index 4f6d083..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/BasicProgressLoggingAwareFormatter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.core.Context;
-import org.gradle.api.logging.StandardOutputListener;
-
-import java.io.IOException;
-
-public class BasicProgressLoggingAwareFormatter extends AbstractProgressLoggingAwareFormatter {
-
-    private enum State {
-        StartOfLine {
-            @Override
-            public State startNewLine(StandardOutputListener target) throws IOException {
-                return this;
-            }
-            @Override
-            public State addCompletion(StandardOutputListener target, String status) throws IOException {
-                if (status.length() == 0) {
-                    return this;
-                }
-                target.onOutput(status);
-                target.onOutput(EOL);
-                return this;
-            }},
-        Description {
-            @Override
-            public State startNewLine(StandardOutputListener target) throws IOException {
-                target.onOutput(EOL);
-                return StartOfLine;
-            }
-            @Override
-            public State addCompletion(StandardOutputListener target, String status) throws IOException {
-                if (status.length() > 0) {
-                    target.onOutput(" ");
-                    target.onOutput(status);
-                }
-                target.onOutput(EOL);
-                return StartOfLine;
-            }};
-
-        public abstract State startNewLine(StandardOutputListener target) throws IOException;
-
-        public abstract State addCompletion(StandardOutputListener target, String status) throws IOException;
-    }
-
-    private State state = State.StartOfLine;
-    private final StandardOutputListener infoTarget;
-    private final StandardOutputListener errorTarget;
-
-    public BasicProgressLoggingAwareFormatter(Context context, StandardOutputListener infoTarget, StandardOutputListener errorTarget) {
-        super(context);
-        this.infoTarget = infoTarget;
-        this.errorTarget = errorTarget;
-    }
-
-    @Override
-    protected void onStart(Operation operation) throws IOException {
-        if (operation.getDescription().length() > 0) {
-            state.startNewLine(infoTarget);
-            infoTarget.onOutput(operation.getDescription());
-            state = State.Description;
-        }
-    }
-
-    @Override
-    protected void onStatusChange(Operation operation) throws IOException {
-    }
-
-    @Override
-    protected void onComplete(Operation operation) throws IOException {
-        state = state.addCompletion(infoTarget, operation.getStatus());
-    }
-
-    @Override
-    protected void onInfoMessage(String message) throws IOException {
-        state = state.startNewLine(infoTarget);
-        infoTarget.onOutput(message);
-    }
-
-    @Override
-    protected void onErrorMessage(String message) throws IOException {
-        state = state.startNewLine(infoTarget);
-        errorTarget.onOutput(message);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Console.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Console.java
deleted file mode 100644
index 4a85483..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Console.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-public interface Console {
-    TextArea getMainArea();
-
-    Label addStatusBar();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ConsoleBackedFormatter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ConsoleBackedFormatter.java
deleted file mode 100644
index da80fe4..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ConsoleBackedFormatter.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.core.Context;
-
-import java.io.IOException;
-import java.util.*;
-
-public class ConsoleBackedFormatter extends AbstractProgressLoggingAwareFormatter {
-    private final Console console;
-    private final Set<Operation> currentOperations = new LinkedHashSet<Operation>();
-    private final Set<Operation> noHeader = new LinkedHashSet<Operation>();
-    private final Label statusBar;
-
-    public ConsoleBackedFormatter(Context context, Console console) {
-        super(context);
-        this.console = console;
-        statusBar = console.addStatusBar();
-    }
-
-    @Override
-    protected void onStart(Operation operation) throws IOException {
-        writeHeaders();
-        currentOperations.add(operation);
-        noHeader.add(operation);
-        updateText();
-    }
-
-    @Override
-    protected void onComplete(Operation operation) throws IOException {
-        currentOperations.remove(operation);
-        boolean hasCompletionStatus = operation.getStatus().length() > 0;
-        boolean hasDescription = operation.getDescription().length() > 0;
-        if (noHeader.remove(operation) || hasCompletionStatus) {
-            StringBuilder builder = new StringBuilder();
-            if (hasDescription) {
-                builder.append(operation.getDescription());
-            }
-            if (hasCompletionStatus) {
-                if (hasDescription) {
-                    builder.append(' ');
-                }
-                builder.append(operation.getStatus());
-            }
-            if (builder.length() > 0) {
-                builder.append(EOL);
-                console.getMainArea().append(builder.toString());
-            }
-        }
-        updateText();
-    }
-
-    @Override
-    protected void onStatusChange(Operation operation) throws IOException {
-        updateText();
-    }
-
-    @Override
-    protected void onInfoMessage(String message) throws IOException {
-        writeHeaders();
-        console.getMainArea().append(message);
-    }
-
-    @Override
-    protected void onErrorMessage(String message) throws IOException {
-        onInfoMessage(message);
-    }
-
-    private void updateText() {
-        StringBuilder builder = new StringBuilder();
-        for (Operation operation : currentOperations) {
-            if (operation.getStatus().length() == 0) {
-                continue;
-            }
-            if (builder.length() > 0) {
-                builder.append(' ');
-            }
-            builder.append("> ");
-            builder.append(operation.getStatus());
-        }
-        statusBar.setText(builder.toString());
-    }
-
-    private void writeHeaders() {
-        for (Operation operation : noHeader) {
-            if (operation.getDescription().length() > 0) {
-                console.getMainArea().append(operation.getDescription() + EOL);
-            }
-        }
-        noHeader.clear();
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingConfigurer.java
deleted file mode 100644
index 1ebdbd5..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingConfigurer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class DefaultLoggingConfigurer implements LoggingConfigurer {
-    private final List<LoggingConfigurer> configurers = new ArrayList<LoggingConfigurer>();
-
-    public DefaultLoggingConfigurer(LoggingConfigurer... configurers) {
-        this.configurers.addAll(Arrays.asList(configurers));
-    }
-
-    public void configure(LogLevel logLevel) {
-        for (LoggingConfigurer configurer : configurers) {
-            configurer.configure(logLevel);
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingManager.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingManager.java
deleted file mode 100644
index 3ce9ab3..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingManager.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.LoggingOutput;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.messaging.concurrent.CompositeStoppable;
-import org.gradle.messaging.concurrent.Stoppable;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultLoggingManager implements LoggingManagerInternal {
-    private boolean started;
-    private final StartableLoggingSystem loggingSystem;
-    private final StartableLoggingSystem stdOutLoggingSystem;
-    private final StartableLoggingSystem stdErrLoggingSystem;
-    private final LoggingOutput loggingOutput;
-    private final Set<StandardOutputListener> stdoutListeners = new LinkedHashSet<StandardOutputListener>();
-    private final Set<StandardOutputListener> stderrListeners = new LinkedHashSet<StandardOutputListener>();
-
-    public DefaultLoggingManager(LoggingSystem loggingSystem, LoggingSystem stdOutLoggingSystem,
-                                 LoggingSystem stdErrLoggingSystem, LoggingOutput loggingOutput) {
-        this.loggingOutput = loggingOutput;
-        this.loggingSystem = new StartableLoggingSystem(loggingSystem, null);
-        this.stdOutLoggingSystem = new StartableLoggingSystem(stdOutLoggingSystem, LogLevel.QUIET);
-        this.stdErrLoggingSystem = new StartableLoggingSystem(stdErrLoggingSystem, LogLevel.ERROR);
-    }
-
-    public DefaultLoggingManager start() {
-        started = true;
-        for (StandardOutputListener stdoutListener : stdoutListeners) {
-            loggingOutput.addStandardOutputListener(stdoutListener);
-        }
-        for (StandardOutputListener stderrListener : stderrListeners) {
-            loggingOutput.addStandardErrorListener(stderrListener);
-        }
-        loggingSystem.start();
-        stdOutLoggingSystem.start();
-        stdErrLoggingSystem.start();
-
-        return this;
-    }
-
-    public DefaultLoggingManager stop() {
-        try {
-            new CompositeStoppable(loggingSystem, stdOutLoggingSystem, stdErrLoggingSystem).stop();
-            for (StandardOutputListener stdoutListener : stdoutListeners) {
-                loggingOutput.removeStandardOutputListener(stdoutListener);
-            }
-            for (StandardOutputListener stderrListener : stderrListeners) {
-                loggingOutput.removeStandardErrorListener(stderrListener);
-            }
-        } finally {
-            started = false;
-        }
-        return this;
-    }
-
-    public DefaultLoggingManager setLevel(LogLevel logLevel) {
-        loggingSystem.setLevel(logLevel);
-        return this;
-    }
-
-    public LogLevel getLevel() {
-        return loggingSystem.level;
-    }
-
-    public LogLevel getStandardOutputCaptureLevel() {
-        return stdOutLoggingSystem.level;
-    }
-
-    public boolean isStandardOutputCaptureEnabled() {
-        return getStandardOutputCaptureLevel() != null;
-    }
-
-    public DefaultLoggingManager captureStandardOutput(LogLevel level) {
-        stdOutLoggingSystem.setLevel(level);
-        return this;
-    }
-
-    public DefaultLoggingManager captureStandardError(LogLevel level) {
-        stdErrLoggingSystem.setLevel(level);
-        return this;
-    }
-
-    public DefaultLoggingManager disableStandardOutputCapture() {
-        stdOutLoggingSystem.disable();
-        stdErrLoggingSystem.disable();
-        return this;
-    }
-
-    public LogLevel getStandardErrorCaptureLevel() {
-        return stdErrLoggingSystem.level;
-    }
-
-    public void addStandardOutputListener(StandardOutputListener listener) {
-        if (stdoutListeners.add(listener) && started) {
-            loggingOutput.addStandardOutputListener(listener);
-        }
-    }
-
-    public void addStandardErrorListener(StandardOutputListener listener) {
-        if (stderrListeners.add(listener) && started) {
-            loggingOutput.addStandardErrorListener(listener);
-        }
-    }
-
-    public void removeStandardOutputListener(StandardOutputListener listener) {
-        if (stdoutListeners.remove(listener) && started) {
-            loggingOutput.removeStandardOutputListener(listener);
-        }
-    }
-
-    public void removeStandardErrorListener(StandardOutputListener listener) {
-        if (stderrListeners.remove(listener) && started) {
-            loggingOutput.removeStandardErrorListener(listener);
-        }
-    }
-
-    private static class StartableLoggingSystem implements Stoppable {
-        private final LoggingSystem loggingSystem;
-        private LogLevel level;
-        private boolean disable;
-        private LoggingSystem.Snapshot originalState;
-
-        private StartableLoggingSystem(LoggingSystem loggingSystem, LogLevel level) {
-            this.loggingSystem = loggingSystem;
-            this.level = level;
-        }
-
-        public void start() {
-            if (disable) {
-                originalState = loggingSystem.off();
-            } else if (level != null) {
-                originalState = loggingSystem.on(level);
-            } else {
-                originalState = loggingSystem.snapshot();
-            }
-        }
-
-        public void setLevel(LogLevel logLevel) {
-            if (this.level == logLevel) {
-                return;
-            }
-
-            this.level = logLevel;
-            disable = false;
-
-            if (originalState == null) {
-                return;
-            }
-            loggingSystem.on(logLevel);
-        }
-
-        public void disable() {
-            level = null;
-            disable = true;
-            if (originalState != null) {
-                loggingSystem.off();
-            }
-        }
-
-        public void stop() {
-            try {
-                if (originalState != null) {
-                    loggingSystem.restore(originalState);
-                }
-            } finally {
-                originalState = null;
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingManagerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingManagerFactory.java
deleted file mode 100644
index 355a05e..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultLoggingManagerFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.LoggingOutput;
-
-public class DefaultLoggingManagerFactory implements LoggingManagerFactory {
-    private final LoggingSystem slfLoggingSystem;
-    private final LoggingSystem stdOutLoggingSystem;
-    private final LoggingSystem stdErrLoggingSystem;
-    private final LoggingOutput loggingOutput;
-
-    public DefaultLoggingManagerFactory(LoggingConfigurer loggingConfigurer, LoggingOutput loggingOutput) {
-        this.loggingOutput = loggingOutput;
-        slfLoggingSystem = new LoggingSystemAdapter(loggingConfigurer);
-        stdOutLoggingSystem = new StdOutLoggingSystem();
-        stdErrLoggingSystem = new StdErrLoggingSystem();
-    }
-
-    public LoggingManagerInternal create() {
-        return new DefaultLoggingManager(slfLoggingSystem, stdOutLoggingSystem, stdErrLoggingSystem, loggingOutput);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultProgressLoggerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultProgressLoggerFactory.java
deleted file mode 100644
index e05fb02..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultProgressLoggerFactory.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.listener.ListenerManager;
-
-public class DefaultProgressLoggerFactory implements ProgressLoggerFactory {
-    private final ListenerManager listenerManager;
-
-    public DefaultProgressLoggerFactory(ListenerManager listenerManager) {
-        this.listenerManager = listenerManager;
-    }
-
-    public ProgressLogger start() {
-        return start("");
-    }
-
-    public ProgressLogger start(String description) {
-        ProgressListener listener = listenerManager.getBroadcaster(ProgressListener.class);
-        ProgressLoggerImpl logger = new ProgressLoggerImpl(description, listener);
-        logger.started();
-        return logger;
-    }
-
-    private static class ProgressLoggerImpl implements ProgressLogger {
-        private final String description;
-        private final ProgressListener listener;
-        private String status = "";
-        private boolean completed;
-
-        public ProgressLoggerImpl(String description, ProgressListener listener) {
-            this.description = description;
-            this.listener = listener;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public String getStatus() {
-            return status;
-        }
-
-        public void started() {
-            listener.started(this);
-        }
-
-        public void progress(String status) {
-            assertNotCompleted();
-            this.status = status;
-            listener.progress(this);
-        }
-
-        public void completed() {
-            completed("");
-        }
-
-        public void completed(String status) {
-            this.status = status;
-            completed = true;
-            listener.completed(this);
-        }
-
-        private void assertNotCompleted() {
-            if (completed) {
-                throw new IllegalStateException("This ProgressLogger has been completed.");
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultStandardOutputRedirector.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultStandardOutputRedirector.java
deleted file mode 100644
index 48e05c6..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/DefaultStandardOutputRedirector.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.Action;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.util.LinePerThreadBufferingOutputStream;
-
-import java.io.PrintStream;
-
-public class DefaultStandardOutputRedirector implements StandardOutputRedirector {
-    private PrintStream originalStdOut;
-    private PrintStream originalStdErr;
-    private final WriteAction stdOut = new WriteAction();
-    private final WriteAction stdErr = new WriteAction();
-    private final PrintStream redirectedStdOut = new LinePerThreadBufferingOutputStream(stdOut, true);
-    private final PrintStream redirectedStdErr = new LinePerThreadBufferingOutputStream(stdErr, true);
-
-    public void redirectStandardOutputTo(StandardOutputListener stdOutDestination) {
-        stdOut.setDestination(stdOutDestination);
-    }
-
-    public void redirectStandardErrorTo(StandardOutputListener stdErrDestination) {
-        stdErr.setDestination(stdErrDestination);
-    }
-
-    public StandardOutputCapture start() {
-        if (stdOut.destination != null) {
-            originalStdOut = System.out;
-            System.setOut(redirectedStdOut);
-        }
-        if (stdErr.destination != null) {
-            originalStdErr = System.err;
-            System.setErr(redirectedStdErr);
-        }
-        return this;
-    }
-
-    public StandardOutputCapture stop() {
-        try {
-            if (originalStdOut != null) {
-                System.setOut(originalStdOut);
-            }
-            if (originalStdErr != null) {
-                System.setErr(originalStdErr);
-            }
-            redirectedStdOut.flush();
-            redirectedStdErr.flush();
-        } finally {
-            originalStdOut = null;
-            originalStdErr = null;
-            stdOut.setDestination(new DiscardAction());
-            stdErr.setDestination(new DiscardAction());
-        }
-        return this;
-    }
-
-    private static class DiscardAction implements StandardOutputListener {
-        public void onOutput(CharSequence output) {
-        }
-    }
-
-    private static class WriteAction implements Action<String> {
-        private StandardOutputListener destination;
-
-        public void execute(String message) {
-            destination.onOutput(message);
-        }
-
-        public void setDestination(StandardOutputListener destination) {
-            this.destination = destination;
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/IvyLoggingAdaper.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/IvyLoggingAdaper.java
deleted file mode 100644
index 4a205d9..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/IvyLoggingAdaper.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.apache.ivy.util.AbstractMessageLogger;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-
-/**
- * This class is for integrating Ivy log statements into our logging system. We don't want to have a dependency on
- * logback. This would be bad for embedded usage. We only want one on slf4j. But slf4j has no constants for log levels.
- * As we want to avoid the execution of if statements for each Ivy request, we use Map which delegates Ivy log
- * statements to Sl4j action classes.
- *
- * @author Hans Dockter
- */
-public class IvyLoggingAdaper extends AbstractMessageLogger {
-    private final Logger logger = Logging.getLogger(IvyLoggingAdaper.class);
-
-    public void log(String msg, int level) {
-        logger.log(Logging.ANT_IVY_2_SLF4J_LEVEL_MAPPER.get(level), msg);
-    }
-
-    public void rawlog(String msg, int level) {
-        log(msg, level);
-    }
-
-    public void doProgress() {
-    }
-
-    public void doEndProgress(String msg) {
-        logger.info(msg);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/JavaUtilLoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/JavaUtilLoggingConfigurer.java
deleted file mode 100644
index 6283a9e..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/JavaUtilLoggingConfigurer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-import org.slf4j.bridge.SLF4JBridgeHandler;
-
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class JavaUtilLoggingConfigurer implements LoggingConfigurer {
-    private boolean configured;
-
-    public void configure(LogLevel logLevel) {
-        if (configured) {
-            return;
-        }
-
-        LogManager.getLogManager().reset();
-        SLF4JBridgeHandler.install();
-        Logger.getLogger("").setLevel(java.util.logging.Level.FINE);
-        configured = true; 
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Label.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Label.java
deleted file mode 100644
index ff42037..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Label.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-public interface Label {
-    void setText(String text);
-
-    void close();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LayoutBasedFormatter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LayoutBasedFormatter.java
deleted file mode 100644
index f28a179..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LayoutBasedFormatter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.Layout;
-import org.gradle.api.logging.StandardOutputListener;
-
-public class LayoutBasedFormatter implements LogEventFormatter {
-    private final Layout<ILoggingEvent> layout;
-    private final StandardOutputListener target;
-
-    public LayoutBasedFormatter(Layout<ILoggingEvent> layout, StandardOutputListener target) {
-        this.layout = layout;
-        this.target = target;
-    }
-
-    public void format(ILoggingEvent event) {
-        target.onOutput(layout.doLayout(event));
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LogEventFormatter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LogEventFormatter.java
deleted file mode 100644
index 62ebeb9..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LogEventFormatter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.classic.spi.ILoggingEvent;
-
-public interface LogEventFormatter {
-    void format(ILoggingEvent event);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java
new file mode 100644
index 0000000..2e6a5f4
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingConfiguration.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging;
+
+import org.gradle.api.logging.LogLevel;
+
+public class LoggingConfiguration {
+    private LogLevel logLevel = LogLevel.LIFECYCLE;
+    private boolean colorOutput = true;
+
+    public LogLevel getLogLevel() {
+        return logLevel;
+    }
+
+    public void setLogLevel(LogLevel logLevel) {
+        this.logLevel = logLevel;
+    }
+
+    public boolean isColorOutput() {
+        return colorOutput;
+    }
+
+    public void setColorOutput(boolean colorOutput) {
+        this.colorOutput = colorOutput;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingConfigurer.java
deleted file mode 100644
index 134d4a2..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingConfigurer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-
-/**
- * @author Hans Dockter
- */
-public interface LoggingConfigurer {
-    void configure(LogLevel logLevel);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerFactory.java
deleted file mode 100644
index 1d399b5..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerFactory.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-public interface LoggingManagerFactory {
-    LoggingManagerInternal create();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerInternal.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerInternal.java
index 9890006..6d00a5f 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerInternal.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingManagerInternal.java
@@ -17,20 +17,16 @@ package org.gradle.logging;
 
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.LoggingManager;
+import org.gradle.logging.internal.LoggingOutputInternal;
 
-public interface LoggingManagerInternal extends LoggingManager, StandardOutputCapture {
-    @Override
+public interface LoggingManagerInternal extends LoggingManager, StandardOutputCapture, LoggingOutputInternal {
     LoggingManagerInternal start();
 
-    @Override
     LoggingManagerInternal stop();
 
-    @Override
     LoggingManagerInternal captureStandardOutput(LogLevel level);
 
-    @Override
     LoggingManagerInternal captureStandardError(LogLevel level);
 
-    @Override
     LoggingManagerInternal setLevel(LogLevel logLevel);
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
index 293fe37..5ee4b57 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingServiceRegistry.java
@@ -16,16 +16,55 @@
 
 package org.gradle.logging;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.project.DefaultServiceRegistry;
+import org.gradle.initialization.CommandLineConverter;
+import org.gradle.logging.internal.*;
+import org.gradle.util.TimeProvider;
+import org.gradle.util.TrueTimeProvider;
 
 /**
  * A {@link org.gradle.api.internal.project.ServiceRegistry} implementation which provides the logging services.
  */
 public class LoggingServiceRegistry extends DefaultServiceRegistry {
-    protected LoggingManagerFactory createLoggingManagerFactory() {
-        Slf4jLoggingConfigurer slf4jLoggingConfigurer = new Slf4jLoggingConfigurer();
-        LoggingConfigurer loggingConfigurer = new DefaultLoggingConfigurer(slf4jLoggingConfigurer,
-                new JavaUtilLoggingConfigurer());
-        return new DefaultLoggingManagerFactory(loggingConfigurer, slf4jLoggingConfigurer);
+    private TextStreamOutputEventListener stdoutListener;
+
+    public LoggingServiceRegistry() {
+        stdoutListener = new TextStreamOutputEventListener(get(OutputEventListener.class));
+    }
+
+    protected CommandLineConverter<LoggingConfiguration> createCommandLineConverter() {
+        return new LoggingCommandLineConverter();
+    }
+
+    protected TimeProvider createTimeProvider() {
+        return new TrueTimeProvider();
+    }
+    
+    protected StdOutLoggingSystem createStdOutLoggingSystem() {
+        return new StdOutLoggingSystem(stdoutListener, get(TimeProvider.class));
+    }
+
+    protected StyledTextOutputFactory createStyledTextOutputFactory() {
+        return new DefaultStyledTextOutputFactory(stdoutListener, get(TimeProvider.class));
+    }
+
+    protected StdErrLoggingSystem createStdErrLoggingSystem() {
+        return new StdErrLoggingSystem(new TextStreamOutputEventListener(get(OutputEventListener.class)), get(TimeProvider.class));
+    }
+
+    protected ProgressLoggerFactory createProgressLoggerFactory() {
+        return new DefaultProgressLoggerFactory(new ProgressLoggingBridge(get(OutputEventListener.class)), get(TimeProvider.class));
+    }
+    
+    protected Factory<LoggingManagerInternal> createLoggingManagerFactory() {
+        OutputEventRenderer renderer = get(OutputEventRenderer.class);
+        Slf4jLoggingConfigurer slf4jConfigurer = new Slf4jLoggingConfigurer(renderer);
+        LoggingConfigurer compositeConfigurer = new DefaultLoggingConfigurer(renderer, slf4jConfigurer, new JavaUtilLoggingConfigurer());
+        return new DefaultLoggingManagerFactory(compositeConfigurer, renderer, get(StdOutLoggingSystem.class), get(StdErrLoggingSystem.class));
+    }
+    
+    protected OutputEventRenderer createOutputEventRenderer() {
+        return new OutputEventRenderer().addStandardOutputAndError();
     }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingSystem.java
deleted file mode 100644
index 625a915..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingSystem.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-
-public interface LoggingSystem {
-    Snapshot snapshot();
-
-    /**
-     * Enables logging for this logging system at the given level.
-     *
-     * @param level The new level.
-     * @return the state of this logging system immediately before the changes are applied.
-     */
-    Snapshot on(LogLevel level);
-
-    /**
-     * Disables logging for this logging system
-     *
-     * @return the state of this logging system immediately before the changes are applied.
-     */
-    Snapshot off();
-
-    void restore(Snapshot state);
-
-    interface Snapshot {
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingSystemAdapter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingSystemAdapter.java
deleted file mode 100644
index 9a9a43f..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/LoggingSystemAdapter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-
-/**
- * Adapts a {@link org.gradle.logging.LoggingConfigurer} to a {@link org.gradle.logging.LoggingSystem}.
- */
-public class LoggingSystemAdapter implements LoggingSystem {
-    private final LoggingConfigurer configurer;
-    private LogLevel logLevel = LogLevel.LIFECYCLE;
-
-    public LoggingSystemAdapter(LoggingConfigurer configurer) {
-        this.configurer = configurer;
-    }
-
-    public Snapshot snapshot() {
-        return new SnapshotImpl(logLevel);
-    }
-
-    public Snapshot off() {
-        return new SnapshotImpl(logLevel);
-    }
-
-    public Snapshot on(LogLevel level) {
-        SnapshotImpl snapshot = new SnapshotImpl(logLevel);
-        setLevel(level);
-        return snapshot;
-    }
-
-    public void restore(Snapshot state) {
-        LogLevel oldLevel = ((SnapshotImpl) state).level;
-        this.logLevel = oldLevel;
-        if (oldLevel != null) {
-            configurer.configure(oldLevel);
-        }
-    }
-
-    private void setLevel(LogLevel level) {
-        configurer.configure(level);
-        this.logLevel = level;
-    }
-
-    private class SnapshotImpl implements Snapshot {
-        private final LogLevel level;
-
-        public SnapshotImpl(LogLevel level) {
-            this.level = level;
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/MarkerFilter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/MarkerFilter.java
deleted file mode 100644
index 2713ecb..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/MarkerFilter.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.filter.Filter;
-import ch.qos.logback.core.spi.FilterReply;
-import org.slf4j.Marker;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * @author Hans Dockter
- */
-public class MarkerFilter extends Filter<ILoggingEvent> {
-    private final List<Marker> markers;
-
-    private FilterReply onMismatch = FilterReply.NEUTRAL;
-
-    public MarkerFilter(Marker... markers) {
-        this.markers = Arrays.asList(markers);
-    }
-
-    public MarkerFilter(FilterReply onMismatch, Marker... markers) {
-        this(markers);
-        this.onMismatch = onMismatch;
-    }
-
-    @Override
-    public FilterReply decide(ILoggingEvent loggingEvent) {
-        Marker marker = loggingEvent.getMarker();
-        if (marker != null) {
-            for (Marker candidate : markers) {
-                if (marker.contains(candidate)) {
-                    return FilterReply.ACCEPT;
-                }
-            }
-        }
-        return onMismatch;
-    }
-
-    public FilterReply getOnMismatch() {
-        return onMismatch;
-    }
-
-    public void setOnMismatch(FilterReply onMismatch) {
-        this.onMismatch = onMismatch;
-    }
-
-    public List getMarkers() {
-        return markers;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/OutputStreamStandardOutputListenerAdapter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/OutputStreamStandardOutputListenerAdapter.java
deleted file mode 100644
index b610f37..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/OutputStreamStandardOutputListenerAdapter.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.logging.StandardOutputListener;
-
-import java.io.Flushable;
-import java.io.IOException;
-import java.io.PrintStream;
-
-public class OutputStreamStandardOutputListenerAdapter implements StandardOutputListener {
-    private final Appendable appendable;
-    private final Flushable flushable;
-
-    public OutputStreamStandardOutputListenerAdapter(PrintStream printStream) {
-        appendable = printStream;
-        flushable = printStream;
-    }
-
-    public void onOutput(CharSequence output) {
-        try {
-            appendable.append(output);
-            flushable.flush();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/PrintStreamLoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/PrintStreamLoggingSystem.java
deleted file mode 100644
index 4d59fb5..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/PrintStreamLoggingSystem.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.gradle.api.Action;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.util.LinePerThreadBufferingOutputStream;
-
-import java.io.PrintStream;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * A {@link org.gradle.logging.LoggingSystem} which routes content written to a PrintStream to the logging system.
- */
-abstract class PrintStreamLoggingSystem implements LoggingSystem {
-    private final AtomicReference<StandardOutputListener> destination
-            = new AtomicReference<StandardOutputListener>();
-    private final PrintStream outstr = new LinePerThreadBufferingOutputStream(new Action<String>() {
-        public void execute(String output) {
-            destination.get().onOutput(output);
-        }
-    });
-    private final Logger logger;
-    private StandardOutputListener original;
-
-    protected PrintStreamLoggingSystem(Logger logger) {
-        this.logger = logger;
-    }
-
-    /**
-     * Returns the current value of the PrintStream
-     */
-    protected abstract PrintStream get();
-
-    /**
-     * Sets the current value of the PrintStream
-     */
-    protected abstract void set(PrintStream printStream);
-
-    public Snapshot snapshot() {
-        return new SnapshotImpl(destination.get());
-    }
-
-    public void restore(Snapshot state) {
-        SnapshotImpl snapshot = (SnapshotImpl) state;
-        install();
-        if (snapshot.listener == null) {
-            destination.set(original);
-        } else {
-            destination.set(snapshot.listener);
-        }
-    }
-
-    public Snapshot on(final LogLevel level) {
-        Snapshot snapshot = snapshot();
-        install();
-        destination.set(new LoggerDestination(level));
-        return snapshot;
-    }
-
-    public Snapshot off() {
-        Snapshot snapshot = snapshot();
-        if (original != null) {
-            outstr.flush();
-            destination.set(original);
-        }
-        return snapshot;
-    }
-
-    private void install() {
-        if (original == null) {
-            PrintStream originalStream = get();
-            original = new PrintStreamDestination(originalStream);
-        }
-        outstr.flush();
-        if (get() != outstr) {
-            set(outstr);
-        }
-    }
-
-    private static class PrintStreamDestination implements StandardOutputListener {
-        private final PrintStream originalStream;
-
-        public PrintStreamDestination(PrintStream originalStream) {
-            this.originalStream = originalStream;
-        }
-
-        public void onOutput(CharSequence output) {
-            originalStream.println(output);
-        }
-    }
-
-    private class LoggerDestination implements StandardOutputListener {
-        private final LogLevel level;
-
-        public LoggerDestination(LogLevel level) {
-            this.level = level;
-        }
-
-        public void onOutput(CharSequence output) {
-            logger.log(level, output.toString());
-        }
-    }
-
-    private static class SnapshotImpl implements Snapshot {
-        private final StandardOutputListener listener;
-
-        public SnapshotImpl(StandardOutputListener listener) {
-            this.listener = listener;
-        }
-    }
-
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressListener.java
deleted file mode 100644
index fbe8793..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressListener.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-public interface ProgressListener {
-    void started(ProgressLogger logger);
-
-    void progress(ProgressLogger logger);
-
-    void completed(ProgressLogger logger);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLogger.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLogger.java
index 7339fea..b786deb 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLogger.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLogger.java
@@ -21,26 +21,26 @@ package org.gradle.logging;
  */
 public interface ProgressLogger {
     /**
-     * Returns the description of the operation.
+     * Returns the description of the operation. The description is generally logged at the start of the operation.
      *
      * @return the description, possibly empty.
      */
     String getDescription();
 
     /**
-     * Logs some progress.
+     * Logs some progress, indicated by a new status.
      *
      * @param status The new status message
      */
     void progress(String status);
 
     /**
-     * Logs the completion of the operation
+     * Logs the completion of the operation, with no final status
      */
     void completed();
 
     /**
-     * Logs the completion of the operation
+     * Logs the completion of the operation, with a final status. This is generally logged along with the description.
      *
      * @param status The final status message
      */
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java
index 8df71c8..199b503 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggerFactory.java
@@ -17,7 +17,20 @@
 package org.gradle.logging;
 
 public interface ProgressLoggerFactory {
-    ProgressLogger start();
-    
-    ProgressLogger start(String description);
+    /**
+     * Starts an operation, with no description.
+     *
+     * @param loggerCategory The logger category.
+     * @return The progress logger for the operation.
+     */
+    ProgressLogger start(String loggerCategory);
+
+    /**
+     * Starts an operation. The description is generally displayed in a log message.
+     *
+     * @param loggerCategory The logger category.
+     * @param description The description of the operation. Can be empty.
+     * @return The progress logger for the operation.
+     */
+    ProgressLogger start(String loggerCategory, String description);
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggingBridge.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggingBridge.java
deleted file mode 100644
index 984af25..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/ProgressLoggingBridge.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-
-public class ProgressLoggingBridge implements ProgressListener {
-    private static final Logger LOGGER = Logging.getLogger(ProgressLoggingBridge.class);
-
-    public void started(ProgressLogger logger) {
-        LOGGER.info(Logging.PROGRESS_STARTED, logger.getDescription());
-    }
-
-    public void completed(ProgressLogger logger) {
-        LOGGER.info(Logging.PROGRESS_COMPLETE, logger.getStatus());
-    }
-
-    public void progress(ProgressLogger logger) {
-        LOGGER.info(Logging.PROGRESS, logger.getStatus());
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Slf4jLoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Slf4jLoggingConfigurer.java
deleted file mode 100644
index 98f95e1..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/Slf4jLoggingConfigurer.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.PatternLayout;
-import ch.qos.logback.classic.filter.LevelFilter;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.AppenderBase;
-import ch.qos.logback.core.Layout;
-import ch.qos.logback.core.filter.Filter;
-import ch.qos.logback.core.spi.FilterReply;
-import org.fusesource.jansi.AnsiConsole;
-import org.gradle.api.logging.*;
-import org.gradle.api.specs.Spec;
-import org.gradle.listener.ListenerBroadcast;
-import org.slf4j.LoggerFactory;
-
-import java.io.FileDescriptor;
-import java.io.PrintStream;
-
-/**
- * @author Hans Dockter
- */
-public class Slf4jLoggingConfigurer implements LoggingConfigurer, LoggingOutput {
-    private final LoggingDestination stdout = new LoggingDestination();
-    private final LoggingDestination stderr = new LoggingDestination();
-    private final Appender errorAppender = new Appender();
-    private final Appender infoAppender = new Appender();
-    private final Spec<FileDescriptor> terminalDetector;
-    private LogEventFormatter consoleFormatter;
-    private LogEventFormatter nonConsoleFormatter;
-    private LogLevel currentLevel;
-    private final PrintStream defaultStdOut;
-
-    public Slf4jLoggingConfigurer() {
-        this(new TerminalDetector());
-    }
-
-    Slf4jLoggingConfigurer(Spec<FileDescriptor> terminalDetector) {
-        this.terminalDetector = terminalDetector;
-        defaultStdOut = System.out;
-    }
-
-    Console createConsole() {
-        if (stdout.terminal) {
-            return new org.gradle.logging.AnsiConsole(AnsiConsole.out(), AnsiConsole.out());
-        }
-        if (stderr.terminal) {
-            return new org.gradle.logging.AnsiConsole(AnsiConsole.err(), AnsiConsole.err());
-        }
-        return null;
-    }
-
-    public void addStandardErrorListener(StandardOutputListener listener) {
-        stderr.addListener(listener);
-    }
-
-    public void removeStandardErrorListener(StandardOutputListener listener) {
-        stderr.removeListener(listener);
-    }
-
-    public void addStandardOutputListener(StandardOutputListener listener) {
-        stdout.addListener(listener);
-    }
-
-    public void removeStandardOutputListener(StandardOutputListener listener) {
-        stdout.removeListener(listener);
-    }
-
-    public void configure(LogLevel logLevel) {
-        if (currentLevel == logLevel) {
-            return;
-        }
-
-        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
-        ch.qos.logback.classic.Logger rootLogger;
-        if (currentLevel == null) {
-            lc.reset();
-
-            stdout.init(FileDescriptor.out, System.out);
-            stderr.init(FileDescriptor.err, System.err);
-
-            Console console = createConsole();
-            consoleFormatter = console == null ? null : new ConsoleBackedFormatter(lc, console);
-            nonConsoleFormatter = new BasicProgressLoggingAwareFormatter(lc, stdout.getBroadcast(),
-                    stderr.getBroadcast());
-
-            errorAppender.setContext(lc);
-            infoAppender.setContext(lc);
-
-            rootLogger = lc.getLogger("ROOT");
-            rootLogger.addAppender(infoAppender);
-            rootLogger.addAppender(errorAppender);
-        } else {
-            rootLogger = lc.getLogger("ROOT");
-        }
-
-        currentLevel = logLevel;
-        errorAppender.stop();
-        infoAppender.stop();
-        errorAppender.clearAllFilters();
-        infoAppender.clearAllFilters();
-
-        errorAppender.addFilter(createLevelFilter(lc, Level.ERROR, FilterReply.ACCEPT, FilterReply.DENY));
-        Level level = Level.INFO;
-
-        setLayouts(logLevel, errorAppender, infoAppender, lc);
-
-        MarkerFilter quietFilter = new MarkerFilter(FilterReply.DENY, Logging.QUIET);
-        infoAppender.addFilter(quietFilter);
-        if (!(logLevel == LogLevel.QUIET)) {
-            quietFilter.setOnMismatch(FilterReply.NEUTRAL);
-            if (logLevel == LogLevel.DEBUG) {
-                level = Level.DEBUG;
-                infoAppender.addFilter(createLevelFilter(lc, Level.INFO, FilterReply.ACCEPT, FilterReply.NEUTRAL));
-                infoAppender.addFilter(createLevelFilter(lc, Level.DEBUG, FilterReply.ACCEPT, FilterReply.NEUTRAL));
-            } else {
-                if (logLevel == LogLevel.INFO) {
-                    level = Level.INFO;
-                    infoAppender.addFilter(createLevelFilter(lc, Level.INFO, FilterReply.ACCEPT, FilterReply.NEUTRAL));
-                } else {
-                    infoAppender.addFilter(new MarkerFilter(Logging.LIFECYCLE, Logging.PROGRESS));
-                }
-            }
-            infoAppender.addFilter(createLevelFilter(lc, Level.WARN, FilterReply.ACCEPT, FilterReply.DENY));
-        }
-        rootLogger.setLevel(level);
-        infoAppender.start();
-        errorAppender.start();
-    }
-
-    private void setLayouts(LogLevel logLevel, Appender errorAppender, Appender nonErrorAppender, LoggerContext lc) {
-        nonErrorAppender.setFormatter(stdout.createFormatter(lc, logLevel));
-        errorAppender.setFormatter(stderr.createFormatter(lc, logLevel));
-    }
-
-    private Filter<ILoggingEvent> createLevelFilter(LoggerContext lc, Level level, FilterReply onMatch,
-                                                    FilterReply onMismatch) {
-        LevelFilter levelFilter = new LevelFilter();
-        levelFilter.setContext(lc);
-        levelFilter.setOnMatch(onMatch);
-        levelFilter.setOnMismatch(onMismatch);
-        levelFilter.setLevel(level);
-        levelFilter.start();
-        return levelFilter;
-    }
-
-    private static class DebugLayout extends PatternLayout {
-        private DebugLayout() {
-            setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex");
-        }
-    }
-
-    private class LoggingDestination {
-        private final ListenerBroadcast<StandardOutputListener> listeners
-                = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
-        private boolean terminal;
-        private PrintStream target;
-
-        private void init(FileDescriptor fileDescriptor, PrintStream target) {
-            this.target = target;
-            terminal = terminalDetector.isSatisfiedBy(fileDescriptor);
-        }
-
-        private LogEventFormatter createFormatter(LoggerContext loggerContext, LogLevel logLevel) {
-            if (logLevel == LogLevel.DEBUG) {
-                Layout<ILoggingEvent> layout = new DebugLayout();
-                layout.setContext(loggerContext);
-                layout.start();
-                return new LayoutBasedFormatter(layout, getBroadcastWithTarget());
-            }
-            if (terminal) {
-                return new LogEventFormatter() {
-                    public void format(ILoggingEvent event) {
-                        consoleFormatter.format(event);
-                        nonConsoleFormatter.format(event);
-                    }
-                };
-            } else {
-                return nonConsoleFormatter;
-            }
-        }
-
-        public void removeListener(StandardOutputListener listener) {
-            listeners.remove(listener);
-        }
-
-        public void addListener(StandardOutputListener listener) {
-            listeners.add(listener);
-        }
-
-        public StandardOutputListener getBroadcast() {
-            if (terminal) {
-                return listeners.getSource();
-            } else {
-                return getBroadcastWithTarget();
-            }
-        }
-
-        private StandardOutputListener getBroadcastWithTarget() {
-            final StandardOutputListener targetListener = new OutputStreamStandardOutputListenerAdapter(target);
-            return new StandardOutputListener() {
-                public void onOutput(CharSequence output) {
-                    targetListener.onOutput(output);
-                    listeners.getSource().onOutput(output);
-                }
-            };
-        }
-    }
-
-    private class Appender extends AppenderBase<ILoggingEvent> {
-        private LogEventFormatter formatter;
-
-        public void setFormatter(LogEventFormatter formatter) {
-            this.formatter = formatter;
-        }
-
-        @Override
-        protected void append(ILoggingEvent event) {
-            try {
-                formatter.format(event);
-            } catch (Throwable t) {
-                // Give up and try stdout
-                t.printStackTrace(defaultStdOut);
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StdErrLoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StdErrLoggingSystem.java
deleted file mode 100644
index d8c6f89..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StdErrLoggingSystem.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.gradle.api.logging.Logging;
-
-import java.io.PrintStream;
-
-class StdErrLoggingSystem extends PrintStreamLoggingSystem {
-    public StdErrLoggingSystem() {
-        super(Logging.getLogger("system.err"));
-    }
-
-    @Override
-    protected PrintStream get() {
-        return System.err;
-    }
-
-    @Override
-    protected void set(PrintStream printStream) {
-        System.setErr(printStream);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StdOutLoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StdOutLoggingSystem.java
deleted file mode 100644
index 91533fa..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StdOutLoggingSystem.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.gradle.api.logging.Logging;
-
-import java.io.PrintStream;
-
-class StdOutLoggingSystem extends PrintStreamLoggingSystem {
-    public StdOutLoggingSystem() {
-        super(Logging.getLogger("system.out"));
-    }
-
-    @Override
-    protected PrintStream get() {
-        return System.out;
-    }
-
-    @Override
-    protected void set(PrintStream printStream) {
-        System.setOut(printStream);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StyledTextOutput.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StyledTextOutput.java
new file mode 100644
index 0000000..6ef4f51
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StyledTextOutput.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging;
+
+/**
+ * Provides streaming of styled text, that is, a stream of text with inline styling information. Implementations are not
+ * required to be thread-safe.
+ */
+public interface StyledTextOutput extends Appendable {
+    enum Style {
+        /**
+         * Regular text.
+         */
+        Normal,
+        /**
+         * A header.
+         */
+        Header,
+        /**
+         * User input
+         */
+        UserInput,
+        /**
+         * An identifier for something
+         */
+        Identifier,
+        /**
+         * The description of something
+         */
+        Description,
+        /**
+         * Operation progress status
+         */
+        ProgressStatus,
+        /**
+         * General purpose success message
+         */
+        Success,
+        /**
+         * General purpose failure message
+         */
+        Failure,
+        /**
+         * General purpose informational text
+         */
+        Info,
+        /**
+         * General purpose error text
+         */
+        Error
+    }
+
+    /**
+     * Appends a character using the current style.
+     *
+     * @param c The character
+     * @return this
+     */
+    StyledTextOutput append(char c);
+
+    /**
+     * Appends a sequence of characters using the current style.
+     *
+     * @param csq The character sequence
+     * @return this.
+     */
+    StyledTextOutput append(CharSequence csq);
+
+    /**
+     * Appends a sequence of characters using the current style.
+     *
+     * @param csq The character sequence
+     * @return this.
+     */
+    StyledTextOutput append(CharSequence csq, int start, int end);
+
+    /**
+     * Switches to a new style. The default style is {@link Style#Normal}.
+     *
+     * @param style The style.
+     * @return this
+     */
+    StyledTextOutput style(Style style);
+
+    /**
+     * Creates a copy of this output which uses the given style. This can be used to generate text in a different style
+     * and then return to the current style. For example:
+     * <pre>
+     * output.style(Info)
+     * output.withStyle(Description).format("%s %s", name, description) // output in Description style
+     * output.println(" text") // output in Info style
+     * </pre>
+     *
+     * @param style The temporary style
+     * @return the copy
+     */
+    StyledTextOutput withStyle(Style style);
+
+    /**
+     * Appends text using the current style.
+     *
+     * @param text The text
+     * @return this
+     */
+    StyledTextOutput text(Object text);
+
+    /**
+     * Appends text using the current style and starts a new line.
+     *
+     * @param text The text
+     * @return this
+     */
+    StyledTextOutput println(Object text);
+
+    /**
+     * Appends a formatted string using the current style.
+     *
+     * @param pattern The pattern string
+     * @param args    The args for the pattern
+     * @return this
+     */
+    StyledTextOutput format(String pattern, Object... args);
+
+    /**
+     * Appends a formatted string using the current style and starts a new line.
+     *
+     * @param pattern The pattern string
+     * @param args    The args for the pattern
+     * @return this
+     */
+    StyledTextOutput formatln(String pattern, Object... args);
+
+    /**
+     * Starts a new line.
+     *
+     * @return this
+     */
+    StyledTextOutput println();
+
+    /**
+     * Appends the stacktrace of the given exception using the current style.
+     *
+     * @param throwable The exception
+     * @return this
+     */
+    StyledTextOutput exception(Throwable throwable);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StyledTextOutputFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StyledTextOutputFactory.java
new file mode 100644
index 0000000..0fddd2b
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/StyledTextOutputFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging;
+
+import org.gradle.api.logging.LogLevel;
+
+public interface StyledTextOutputFactory {
+    /**
+     * Creates a {@code StyledTextOutput} with the given category and the standard output log level.
+     *
+     * @param logCategory The log category.
+     * @return the output
+     */
+    StyledTextOutput create(String logCategory);
+
+    /**
+     * Creates a {@code StyledTextOutput} with the given category and the standard output log level.
+     *
+     * @param logCategory The log category.
+     * @return the output
+     */
+
+    StyledTextOutput create(Class logCategory);
+
+    /**
+     * Creates a {@code StyledTextOutput} with the given category and log level.
+     *
+     * @param logCategory The log category.
+     * @param logLevel The log level. Can be null to use the standard output log level.
+     * @return the output
+     */
+    StyledTextOutput create(Class logCategory, LogLevel logLevel);
+
+    /**
+     * Creates a {@code StyledTextOutput} with the given category and log level.
+     *
+     * @param logCategory The log category.
+     * @param logLevel The log level. Can be null to use the standard output log level.
+     * @return the output
+     */
+    StyledTextOutput create(String logCategory, LogLevel logLevel);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/TerminalDetector.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/TerminalDetector.java
deleted file mode 100644
index 6a23677..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/TerminalDetector.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import org.fusesource.jansi.WindowsAnsiOutputStream;
-import org.gradle.api.specs.Spec;
-import org.gradle.util.OperatingSystem;
-import org.gradle.util.PosixUtil;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-public class TerminalDetector implements Spec<FileDescriptor> {
-    public boolean isSatisfiedBy(FileDescriptor element) {
-        if (OperatingSystem.current().isWindows()) {
-            // Use Jansi's detection mechanism
-            try {
-                new WindowsAnsiOutputStream(new ByteArrayOutputStream());
-            } catch (IOException ignore) {
-                // Not attached to a console
-                return false;
-            }
-        }
-
-        // Use jna-posix
-        return PosixUtil.current().isatty(element);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/TextArea.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/TextArea.java
deleted file mode 100644
index 654fdcd..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/TextArea.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-public interface TextArea {
-    void append(CharSequence text);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
new file mode 100644
index 0000000..36d20d1
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutput.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.logging.StyledTextOutput;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public abstract class AbstractStyledTextOutput implements StyledTextOutput, StandardOutputListener {
+    private static final String EOL = System.getProperty("line.separator");
+    private Style style = Style.Normal;
+
+    public StyledTextOutput append(char c) {
+        text(String.valueOf(c));
+        return this;
+    }
+
+    public StyledTextOutput append(CharSequence csq) {
+        text(csq == null ? "null" : csq);
+        return this;
+    }
+
+    public StyledTextOutput append(CharSequence csq, int start, int end) {
+        text(csq == null ? "null" : csq.subSequence(start, end));
+        return this;
+    }
+
+    public StyledTextOutput format(String pattern, Object... args) {
+        text(String.format(pattern, args));
+        return this;
+    }
+
+    public StyledTextOutput println(Object text) {
+        text(text);
+        println();
+        return this;
+    }
+
+    public StyledTextOutput formatln(String pattern, Object... args) {
+        format(pattern, args);
+        println();
+        return this;
+    }
+
+    public void onOutput(CharSequence output) {
+        text(output);
+    }
+
+    public StyledTextOutput println() {
+        text(EOL);
+        return this;
+    }
+
+    public StyledTextOutput text(Object text) {
+        doAppend(text == null ? "null" : text.toString());
+        return this;
+    }
+
+    public StyledTextOutput exception(Throwable throwable) {
+        StringWriter out = new StringWriter();
+        PrintWriter writer = new PrintWriter(out);
+        throwable.printStackTrace(writer);
+        writer.close();
+        text(out.toString());
+        return this;
+    }
+
+    public StyledTextOutput withStyle(Style style) {
+        return new StyleOverrideTextOutput(style, this);
+    }
+
+    public StyledTextOutput style(Style style) {
+        if (style != this.style) {
+            this.style = style;
+            doStyleChange(style);
+        }
+        return this;
+    }
+
+    public Style getStyle() {
+        return style;
+    }
+    
+    protected abstract void doAppend(String text);
+
+    protected void doStyleChange(Style style) {
+    }
+
+    private static class StyleOverrideTextOutput implements StyledTextOutput {
+        private final Style style;
+        private final AbstractStyledTextOutput textOutput;
+
+        public StyleOverrideTextOutput(Style style, AbstractStyledTextOutput textOutput) {
+            this.style = style;
+            this.textOutput = textOutput;
+        }
+
+        public StyledTextOutput append(char c) {
+            Style original = textOutput.getStyle();
+            textOutput.style(style).append(c).style(original);
+            return this;
+        }
+
+        public StyledTextOutput append(CharSequence csq) {
+            Style original = textOutput.getStyle();
+            textOutput.style(style).append(csq).style(original);
+            return this;
+        }
+
+        public StyledTextOutput append(CharSequence csq, int start, int end) {
+            throw new UnsupportedOperationException();
+        }
+
+        public StyledTextOutput style(Style style) {
+            throw new UnsupportedOperationException();
+        }
+
+        public StyledTextOutput withStyle(Style style) {
+            throw new UnsupportedOperationException();
+        }
+
+        public StyledTextOutput text(Object text) {
+            Style original = textOutput.getStyle();
+            textOutput.style(style).text(text).style(original);
+            return this;
+        }
+
+        public StyledTextOutput println(Object text) {
+            Style original = textOutput.getStyle();
+            textOutput.style(style).text(text).style(original).println();
+            return this;
+        }
+
+        public StyledTextOutput format(String pattern, Object... args) {
+            Style original = textOutput.getStyle();
+            textOutput.style(style).format(pattern, args).style(original);
+            return this;
+        }
+
+        public StyledTextOutput formatln(String pattern, Object... args) {
+            throw new UnsupportedOperationException();
+        }
+
+        public StyledTextOutput println() {
+            throw new UnsupportedOperationException();
+        }
+
+        public StyledTextOutput exception(Throwable throwable) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutputFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutputFactory.java
new file mode 100644
index 0000000..69752c1
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AbstractStyledTextOutputFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+
+public abstract class AbstractStyledTextOutputFactory implements StyledTextOutputFactory {
+    public StyledTextOutput create(Class logCategory) {
+        return create(logCategory.getName());
+    }
+
+    public StyledTextOutput create(String logCategory) {
+        return create(logCategory, null);
+    }
+
+    public StyledTextOutput create(Class logCategory, LogLevel logLevel) {
+        return create(logCategory.getName(), logLevel);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
new file mode 100644
index 0000000..956bf20
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/AnsiConsole.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.fusesource.jansi.Ansi;
+import org.gradle.api.Action;
+import org.gradle.api.UncheckedIOException;
+
+import java.io.Flushable;
+import java.io.IOException;
+import java.util.Iterator;
+
+public class AnsiConsole implements Console {
+    private final static String EOL = System.getProperty("line.separator");
+    private final Appendable target;
+    private final Flushable flushable;
+    private LabelImpl statusBar;
+    private final TextAreaImpl textArea;
+    private final Screen container;
+    private final ColorMap colorMap;
+
+    public AnsiConsole(Appendable target, Flushable flushable, ColorMap colorMap) {
+        this.target = target;
+        this.flushable = flushable;
+        this.colorMap = colorMap;
+        container = new Screen();
+        textArea = new TextAreaImpl(container);
+    }
+
+    public Label getStatusBar() {
+        if (statusBar == null) {
+            statusBar = new LabelImpl(container);
+            render(new Action<Ansi>() {
+                public void execute(Ansi ansi) {
+                    textArea.onDeactivate(ansi);
+                    statusBar.onActivate(ansi);
+                }
+            });
+        }
+        return statusBar;
+    }
+
+    public TextArea getMainArea() {
+        return textArea;
+    }
+
+    private void render(Action<Ansi> action) {
+        Ansi ansi = createAnsi();
+        action.execute(ansi);
+        try {
+            target.append(ansi.toString());
+            flushable.flush();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    Ansi createAnsi() {
+        return Ansi.ansi();
+    }
+
+    private interface Container {
+        void redraw(Widget widget, Action<Ansi> drawOperation);
+
+        void close(Widget widget);
+    }
+
+    private interface Widget {
+        /**
+         * Called when this widget becomes the active widget. The active widget is the widget at the bottom of the
+         * screen. When called, the cursor will be positioned at the left edge of bottom-most line of the screen.
+         */
+        void onActivate(Ansi ansi);
+
+        /**
+         * Called when this widget is no longer the active widget. Should Remove content of this widget from the last
+         * line of the screen and leave the cursor at left edge of bottom-most line.
+         */
+        void onDeactivate(Ansi ansi);
+    }
+
+    private class Screen implements Container {
+        public void redraw(Widget widget, final Action<Ansi> drawOperation) {
+            if (widget == textArea) {
+                render(new Action<Ansi>() {
+                    public void execute(Ansi ansi) {
+                        if (statusBar != null) {
+                            statusBar.onDeactivate(ansi);
+                            textArea.onActivate(ansi);
+                        }
+                        drawOperation.execute(ansi);
+                        if (statusBar != null) {
+                            textArea.onDeactivate(ansi);
+                            statusBar.onActivate(ansi);
+                        }
+                    }
+                });
+            } else {
+                assert widget == statusBar;
+                render(new Action<Ansi>() {
+                    public void execute(Ansi ansi) {
+                        drawOperation.execute(ansi);
+                    }
+                });
+            }
+        }
+
+        public void close(Widget widget) {
+            if (widget == textArea) {
+                throw new UnsupportedOperationException();
+            }
+            if (widget == statusBar) {
+                render(new Action<Ansi>() {
+                    public void execute(Ansi ansi) {
+                        statusBar.onDeactivate(ansi);
+                        textArea.onActivate(ansi);
+                        statusBar = null;
+                    }
+                });
+            }
+        }
+    }
+
+    private class LabelImpl implements Label, Widget {
+        private final Container container;
+        private String text = "";
+        private String displayedText = "";
+
+        public LabelImpl(Container container) {
+            this.container = container;
+        }
+
+        public void setText(String text) {
+            if (text.equals(this.text)) {
+                return;
+            }
+            this.text = text;
+            container.redraw(this, new Action<Ansi>() {
+                public void execute(Ansi ansi) {
+                    draw(ansi);
+                }
+            });
+        }
+
+        public void close() {
+            container.close(this);
+        }
+
+        public void onDeactivate(Ansi ansi) {
+            if (displayedText.length() > 0) {
+                ansi.cursorLeft(displayedText.length());
+                ansi.eraseLine(Ansi.Erase.FORWARD);
+                displayedText = "";
+            }
+        }
+
+        public void onActivate(Ansi ansi) {
+            draw(ansi);
+        }
+
+        public void draw(Ansi ansi) {
+            String prefix = StringUtils.getCommonPrefix(new String[]{text, displayedText});
+            if (prefix.length() < displayedText.length()) {
+                ansi.cursorLeft(displayedText.length() - prefix.length());
+            }
+            if (prefix.length() < text.length()) {
+                ColorMap.Color color = colorMap.getStatusBarColor();
+                color.on(ansi);
+                ansi.a(text.substring(prefix.length()));
+                color.off(ansi);
+            }
+            if (displayedText.length() > text.length()) {
+                ansi.eraseLine(Ansi.Erase.FORWARD);
+            }
+            displayedText = text;
+        }
+    }
+
+    private class TextAreaImpl extends AbstractStyledTextOutput implements TextArea, Widget {
+        private final Container container;
+        private int width;
+        boolean extraEol;
+
+        private TextAreaImpl(Container container) {
+            this.container = container;
+        }
+
+        public void onDeactivate(Ansi ansi) {
+            if (width > 0) {
+                ansi.newline();
+                extraEol = true;
+            }
+        }
+
+        public void onActivate(Ansi ansi) {
+            if (extraEol) {
+                ansi.cursorUp(1);
+                ansi.cursorRight(width);
+                extraEol = false;
+            }
+        }
+
+        @Override
+        protected void doAppend(final String text) {
+            if (text.length() == 0) {
+                return;
+            }
+            container.redraw(this, new Action<Ansi>() {
+                public void execute(Ansi ansi) {
+                    ColorMap.Color color = colorMap.getColourFor(getStyle());
+                    color.on(ansi);
+
+                    Iterator<String> tokenizer = new LineSplitter(text);
+                    while (tokenizer.hasNext()) {
+                        String token = tokenizer.next();
+                        if (token.equals(EOL)) {
+                            width = 0;
+                            extraEol = false;
+                        } else {
+                            width += token.length();
+                        }
+                        ansi.a(token);
+                    }
+
+                    color.off(ansi);
+                }
+            });
+        }
+    }
+
+    private static class LineSplitter implements Iterator<String> {
+        private final CharSequence text;
+        private int start;
+        private int end;
+
+        private LineSplitter(CharSequence text) {
+            this.text = text;
+            findNext();
+        }
+
+        public boolean findNext() {
+            if (end == text.length()) {
+                start = -1;
+                return false;
+            }
+            if (startsWithEol(text, end)) {
+                start = end;
+                end = start + EOL.length();
+                return true;
+            }
+            int pos = end;
+            while (pos < text.length()) {
+                if (startsWithEol(text, pos)) {
+                    start = end;
+                    end = pos;
+                    return true;
+                }
+                pos++;
+            }
+            start = end;
+            end = text.length();
+            return true;
+        }
+
+        private boolean startsWithEol(CharSequence text, int startAt) {
+            if (startAt + EOL.length() > text.length()) {
+                return false;
+            }
+            for (int i = 0; i < EOL.length(); i++) {
+                if (EOL.charAt(i) != text.charAt(startAt + i)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public boolean hasNext() {
+            return start >= 0;
+        }
+
+        public String next() {
+            CharSequence next = text.subSequence(start, end);
+            findNext();
+            return next.toString();
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/CategorisedOutputEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/CategorisedOutputEvent.java
new file mode 100644
index 0000000..c5e3e1e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/CategorisedOutputEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+public class CategorisedOutputEvent extends OutputEvent {
+    private final String category;
+    private final LogLevel logLevel;
+    private final long timestamp;
+
+    public CategorisedOutputEvent(long timestamp, String category, LogLevel logLevel) {
+        this.timestamp = timestamp;
+        this.category = category;
+        this.logLevel = logLevel;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public LogLevel getLogLevel() {
+        return logLevel;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ColorMap.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ColorMap.java
new file mode 100644
index 0000000..d56f9a2
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ColorMap.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.fusesource.jansi.Ansi;
+import org.gradle.logging.StyledTextOutput;
+
+public interface ColorMap {
+    Color getColourFor(StyledTextOutput.Style style);
+
+    Color getStatusBarColor();
+    
+    interface Color {
+        void on(Ansi ansi);
+
+        void off(Ansi ansi);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Console.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Console.java
new file mode 100644
index 0000000..87a4385
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Console.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+public interface Console {
+    TextArea getMainArea();
+
+    Label getStatusBar();
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java
new file mode 100644
index 0000000..8ef3cf7
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ConsoleBackedProgressRenderer.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import java.util.LinkedList;
+
+public class ConsoleBackedProgressRenderer implements OutputEventListener {
+    private final OutputEventListener listener;
+    private final Console console;
+    private final LinkedList<String> operations = new LinkedList<String>();
+    private Label statusBar;
+
+    public ConsoleBackedProgressRenderer(OutputEventListener listener, Console console) {
+        this.listener = listener;
+        this.console = console;
+    }
+
+    public void onOutput(OutputEvent event) {
+        if (event instanceof ProgressStartEvent) {
+            operations.addLast("");
+        } else if (event instanceof ProgressCompleteEvent) {
+            operations.removeLast();
+            updateText();
+        } else if (event instanceof ProgressEvent) {
+            ProgressEvent progressEvent = (ProgressEvent) event;
+            operations.removeLast();
+            operations.addLast(progressEvent.getStatus());
+            updateText();
+        }
+        listener.onOutput(event);
+    }
+
+    private void updateText() {
+        StringBuilder builder = new StringBuilder();
+        for (String operation : operations) {
+            if (operation.length() == 0) {
+                continue;
+            }
+            if (builder.length() > 0) {
+                builder.append(' ');
+            }
+            builder.append("> ");
+            builder.append(operation);
+        }
+        if (statusBar == null) {
+            statusBar = console.getStatusBar();
+        }
+        statusBar.setText(builder.toString());
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java
new file mode 100644
index 0000000..b3681ec
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultColorMap.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.fusesource.jansi.Ansi;
+import org.gradle.logging.StyledTextOutput;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.fusesource.jansi.Ansi.Attribute;
+import static org.fusesource.jansi.Ansi.Attribute.*;
+import static org.fusesource.jansi.Ansi.Attribute.ITALIC;
+import static org.fusesource.jansi.Ansi.Color.*;
+import static org.gradle.logging.StyledTextOutput.Style.*;
+import static org.gradle.logging.StyledTextOutput.Style.Success;
+
+public class DefaultColorMap implements ColorMap {
+    private static final String STATUSBAR = "statusbar";
+    private static final String BOLD = "bold";
+    private final Map<String, String> defaults = new HashMap<String, String>();
+    private final Map<String, Color> colors = new HashMap<String, Color>();
+    private boolean useColor = true;
+    private final Color noDecoration = new Color() {
+        public void on(Ansi ansi) {
+        }
+
+        public void off(Ansi ansi) {
+        }
+    };
+
+    public DefaultColorMap() {
+        addDefault(Info, "yellow");
+        addDefault(Error, "default");
+        addDefault(Header, "default");
+        addDefault(Description, "yellow");
+        addDefault(ProgressStatus, "yellow");
+        addDefault(Identifier, "green");
+        addDefault(UserInput, "bold");
+        addDefault(Success, "default");
+        addDefault(Failure, "red");
+        addDefault(STATUSBAR, "bold");
+    }
+
+    private void addDefault(StyledTextOutput.Style style, String color) {
+        addDefault(style.name().toLowerCase(), color);
+    }
+
+    private void addDefault(String style, String color) {
+        defaults.put(style, color);
+    }
+
+    public void setUseColor(boolean useColor) {
+        this.useColor = useColor;
+    }
+
+    public Color getStatusBarColor() {
+        return getColor(STATUSBAR);
+    }
+
+    public Color getColourFor(StyledTextOutput.Style style) {
+        return getColor(style.name().toLowerCase());
+    }
+
+    private Color getColor(String style) {
+        if (!useColor) {
+            return noDecoration;
+        }
+
+        Color color = colors.get(style);
+        if (color == null) {
+            color = createColor(style);
+            colors.put(style, color);
+        }
+
+        return color;
+    }
+
+    private Color createColor(String style) {
+        String colorSpec = System.getProperty(String.format("org.gradle.color.%s", style), defaults.get(style));
+
+        if (colorSpec != null) {
+            if (colorSpec.equalsIgnoreCase(BOLD)) {
+                String terminalProgram = System.getenv("TERM_PROGRAM");
+                if (terminalProgram != null && terminalProgram.equals("iTerm.app")) {
+                    // iTerm displays bold as red (by default), so don't bother
+                    return noDecoration;
+                }
+                return new AttributeColor(INTENSITY_BOLD, INTENSITY_BOLD_OFF);
+            }
+            if (colorSpec.equalsIgnoreCase("reverse")) {
+                return new AttributeColor(NEGATIVE_ON, NEGATIVE_OFF);
+            }
+            if (colorSpec.equalsIgnoreCase("italic")) {
+                return new AttributeColor(ITALIC, ITALIC_OFF);
+            }
+
+            Ansi.Color ansiColor = Ansi.Color.valueOf(colorSpec.toUpperCase());
+            if (ansiColor != DEFAULT) {
+                return new ForegroundColor(ansiColor);
+            }
+        }
+
+        return noDecoration;
+    }
+
+    private static class ForegroundColor implements Color {
+        private final Ansi.Color ansiColor;
+
+        public ForegroundColor(Ansi.Color ansiColor) {
+            this.ansiColor = ansiColor;
+        }
+
+        public void on(Ansi ansi) {
+            ansi.fg(ansiColor);
+        }
+
+        public void off(Ansi ansi) {
+            ansi.fg(DEFAULT);
+        }
+    }
+
+    private static class AttributeColor implements Color {
+        private final Ansi.Attribute on;
+        private final Ansi.Attribute off;
+
+        public AttributeColor(Attribute on, Attribute off) {
+            this.on = on;
+            this.off = off;
+        }
+
+        public void on(Ansi ansi) {
+            ansi.a(on);
+        }
+
+        public void off(Ansi ansi) {
+            ansi.a(off);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingConfigurer.java
new file mode 100644
index 0000000..f06d237
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingConfigurer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class DefaultLoggingConfigurer implements LoggingConfigurer {
+    private final List<LoggingConfigurer> configurers = new ArrayList<LoggingConfigurer>();
+
+    public DefaultLoggingConfigurer(LoggingConfigurer... configurers) {
+        this.configurers.addAll(Arrays.asList(configurers));
+    }
+
+    public void configure(LogLevel logLevel) {
+        for (LoggingConfigurer configurer : configurers) {
+            configurer.configure(logLevel);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java
new file mode 100644
index 0000000..1d13ec5
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManager.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.messaging.concurrent.CompositeStoppable;
+import org.gradle.messaging.concurrent.Stoppable;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultLoggingManager implements LoggingManagerInternal {
+    private boolean started;
+    private final StartableLoggingSystem loggingSystem;
+    private final StartableLoggingSystem stdOutLoggingSystem;
+    private final StartableLoggingSystem stdErrLoggingSystem;
+    private final LoggingOutputInternal loggingOutput;
+    private final Set<StandardOutputListener> stdoutListeners = new LinkedHashSet<StandardOutputListener>();
+    private final Set<StandardOutputListener> stderrListeners = new LinkedHashSet<StandardOutputListener>();
+
+    public DefaultLoggingManager(LoggingSystem loggingSystem, LoggingSystem stdOutLoggingSystem,
+                                 LoggingSystem stdErrLoggingSystem, LoggingOutputInternal loggingOutput) {
+        this.loggingOutput = loggingOutput;
+        this.loggingSystem = new StartableLoggingSystem(loggingSystem, null);
+        this.stdOutLoggingSystem = new StartableLoggingSystem(stdOutLoggingSystem, LogLevel.QUIET);
+        this.stdErrLoggingSystem = new StartableLoggingSystem(stdErrLoggingSystem, LogLevel.ERROR);
+    }
+
+    public DefaultLoggingManager start() {
+        started = true;
+        for (StandardOutputListener stdoutListener : stdoutListeners) {
+            loggingOutput.addStandardOutputListener(stdoutListener);
+        }
+        for (StandardOutputListener stderrListener : stderrListeners) {
+            loggingOutput.addStandardErrorListener(stderrListener);
+        }
+        loggingSystem.start();
+        stdOutLoggingSystem.start();
+        stdErrLoggingSystem.start();
+
+        return this;
+    }
+
+    public DefaultLoggingManager stop() {
+        try {
+            new CompositeStoppable(loggingSystem, stdOutLoggingSystem, stdErrLoggingSystem).stop();
+            for (StandardOutputListener stdoutListener : stdoutListeners) {
+                loggingOutput.removeStandardOutputListener(stdoutListener);
+            }
+            for (StandardOutputListener stderrListener : stderrListeners) {
+                loggingOutput.removeStandardErrorListener(stderrListener);
+            }
+        } finally {
+            started = false;
+        }
+        return this;
+    }
+
+    public DefaultLoggingManager setLevel(LogLevel logLevel) {
+        loggingSystem.setLevel(logLevel);
+        return this;
+    }
+
+    public LogLevel getLevel() {
+        return loggingSystem.level;
+    }
+
+    public LogLevel getStandardOutputCaptureLevel() {
+        return stdOutLoggingSystem.level;
+    }
+
+    public boolean isStandardOutputCaptureEnabled() {
+        return getStandardOutputCaptureLevel() != null;
+    }
+
+    public DefaultLoggingManager captureStandardOutput(LogLevel level) {
+        stdOutLoggingSystem.setLevel(level);
+        return this;
+    }
+
+    public DefaultLoggingManager captureStandardError(LogLevel level) {
+        stdErrLoggingSystem.setLevel(level);
+        return this;
+    }
+
+    public DefaultLoggingManager disableStandardOutputCapture() {
+        stdOutLoggingSystem.disable();
+        stdErrLoggingSystem.disable();
+        return this;
+    }
+
+    public LogLevel getStandardErrorCaptureLevel() {
+        return stdErrLoggingSystem.level;
+    }
+
+    public void addStandardOutputListener(StandardOutputListener listener) {
+        if (stdoutListeners.add(listener) && started) {
+            loggingOutput.addStandardOutputListener(listener);
+        }
+    }
+
+    public void addStandardErrorListener(StandardOutputListener listener) {
+        if (stderrListeners.add(listener) && started) {
+            loggingOutput.addStandardErrorListener(listener);
+        }
+    }
+
+    public void removeStandardOutputListener(StandardOutputListener listener) {
+        if (stdoutListeners.remove(listener) && started) {
+            loggingOutput.removeStandardOutputListener(listener);
+        }
+    }
+
+    public void removeStandardErrorListener(StandardOutputListener listener) {
+        if (stderrListeners.remove(listener) && started) {
+            loggingOutput.removeStandardErrorListener(listener);
+        }
+    }
+
+    public void addOutputEventListener(OutputEventListener listener) {
+        loggingOutput.addOutputEventListener(listener);
+    }
+
+    public void removeOutputEventListener(OutputEventListener listener) {
+        loggingOutput.removeOutputEventListener(listener);
+    }
+
+    public void colorStdOutAndStdErr(boolean colorOutput) {
+        loggingOutput.colorStdOutAndStdErr(colorOutput);
+    }
+
+    private static class StartableLoggingSystem implements Stoppable {
+        private final LoggingSystem loggingSystem;
+        private LogLevel level;
+        private boolean disable;
+        private LoggingSystem.Snapshot originalState;
+
+        private StartableLoggingSystem(LoggingSystem loggingSystem, LogLevel level) {
+            this.loggingSystem = loggingSystem;
+            this.level = level;
+        }
+
+        public void start() {
+            if (disable) {
+                originalState = loggingSystem.off();
+            } else if (level != null) {
+                originalState = loggingSystem.on(level);
+            } else {
+                originalState = loggingSystem.snapshot();
+            }
+        }
+
+        public void setLevel(LogLevel logLevel) {
+            if (this.level == logLevel) {
+                return;
+            }
+
+            this.level = logLevel;
+            disable = false;
+
+            if (originalState == null) {
+                return;
+            }
+            loggingSystem.on(logLevel);
+        }
+
+        public void disable() {
+            level = null;
+            disable = true;
+            if (originalState != null) {
+                loggingSystem.off();
+            }
+        }
+
+        public void stop() {
+            try {
+                if (originalState != null) {
+                    loggingSystem.restore(originalState);
+                }
+            } finally {
+                originalState = null;
+            }
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManagerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManagerFactory.java
new file mode 100644
index 0000000..f09f605
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultLoggingManagerFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.internal.Factory;
+import org.gradle.logging.LoggingManagerInternal;
+
+public class DefaultLoggingManagerFactory implements Factory<LoggingManagerInternal> {
+    private final LoggingSystem slfLoggingSystem;
+    private final LoggingSystem stdOutLoggingSystem;
+    private final LoggingSystem stdErrLoggingSystem;
+    private final LoggingOutputInternal loggingOutput;
+
+    public DefaultLoggingManagerFactory(LoggingConfigurer loggingConfigurer, LoggingOutputInternal loggingOutput, LoggingSystem stdOutLoggingSystem, LoggingSystem stdErrLoggingSystem) {
+        this.loggingOutput = loggingOutput;
+        this.stdOutLoggingSystem = stdOutLoggingSystem;
+        this.stdErrLoggingSystem = stdErrLoggingSystem;
+        slfLoggingSystem = new LoggingSystemAdapter(loggingConfigurer);
+    }
+
+    public LoggingManagerInternal create() {
+        return new DefaultLoggingManager(slfLoggingSystem, stdOutLoggingSystem, stdErrLoggingSystem, loggingOutput);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java
new file mode 100644
index 0000000..43e895f
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactory.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.logging.ProgressLogger;
+import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.util.TimeProvider;
+
+public class DefaultProgressLoggerFactory implements ProgressLoggerFactory {
+    private final ProgressListener progressListener;
+    private final TimeProvider timeProvider;
+
+    public DefaultProgressLoggerFactory(ProgressListener progressListener, TimeProvider timeProvider) {
+        this.progressListener = progressListener;
+        this.timeProvider = timeProvider;
+    }
+
+    public ProgressLogger start(String loggerCategory) {
+        return start(loggerCategory, "");
+    }
+
+    public ProgressLogger start(String loggerCategory, String description) {
+        ProgressLoggerImpl logger = new ProgressLoggerImpl(loggerCategory, description, progressListener, timeProvider);
+        logger.started();
+        return logger;
+    }
+
+    private static class ProgressLoggerImpl implements ProgressLogger {
+        private final String category;
+        private final String description;
+        private final ProgressListener listener;
+        private final TimeProvider timeProvider;
+        private String status = "";
+        private boolean completed;
+
+        public ProgressLoggerImpl(String category, String description, ProgressListener listener, TimeProvider timeProvider) {
+            this.category = category;
+            this.description = description;
+            this.listener = listener;
+            this.timeProvider = timeProvider;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+
+        public void started() {
+            listener.started(new ProgressStartEvent(timeProvider.getCurrentTime(), category, description));
+        }
+
+        public void progress(String status) {
+            assertNotCompleted();
+            this.status = status;
+            listener.progress(new ProgressEvent(timeProvider.getCurrentTime(), category, status));
+        }
+
+        public void completed() {
+            completed("");
+        }
+
+        public void completed(String status) {
+            this.status = status;
+            completed = true;
+            listener.completed(new ProgressCompleteEvent(timeProvider.getCurrentTime(), category, status));
+        }
+
+        private void assertNotCompleted() {
+            if (completed) {
+                throw new IllegalStateException("This ProgressLogger has been completed.");
+            }
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirector.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirector.java
new file mode 100644
index 0000000..4a47107
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirector.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.logging.StandardOutputCapture;
+import org.gradle.logging.StandardOutputRedirector;
+import org.gradle.util.LinePerThreadBufferingOutputStream;
+
+import java.io.PrintStream;
+
+public class DefaultStandardOutputRedirector implements StandardOutputRedirector {
+    private PrintStream originalStdOut;
+    private PrintStream originalStdErr;
+    private final WriteAction stdOut = new WriteAction();
+    private final WriteAction stdErr = new WriteAction();
+    private final PrintStream redirectedStdOut = new LinePerThreadBufferingOutputStream(stdOut, true);
+    private final PrintStream redirectedStdErr = new LinePerThreadBufferingOutputStream(stdErr, true);
+
+    public void redirectStandardOutputTo(StandardOutputListener stdOutDestination) {
+        stdOut.setDestination(stdOutDestination);
+    }
+
+    public void redirectStandardErrorTo(StandardOutputListener stdErrDestination) {
+        stdErr.setDestination(stdErrDestination);
+    }
+
+    public StandardOutputCapture start() {
+        if (stdOut.destination != null) {
+            originalStdOut = System.out;
+            System.setOut(redirectedStdOut);
+        }
+        if (stdErr.destination != null) {
+            originalStdErr = System.err;
+            System.setErr(redirectedStdErr);
+        }
+        return this;
+    }
+
+    public StandardOutputCapture stop() {
+        try {
+            if (originalStdOut != null) {
+                System.setOut(originalStdOut);
+            }
+            if (originalStdErr != null) {
+                System.setErr(originalStdErr);
+            }
+            redirectedStdOut.flush();
+            redirectedStdErr.flush();
+        } finally {
+            originalStdOut = null;
+            originalStdErr = null;
+            stdOut.setDestination(new DiscardAction());
+            stdErr.setDestination(new DiscardAction());
+        }
+        return this;
+    }
+
+    private static class DiscardAction implements StandardOutputListener {
+        public void onOutput(CharSequence output) {
+        }
+    }
+
+    private static class WriteAction implements Action<String> {
+        private StandardOutputListener destination;
+
+        public void execute(String message) {
+            destination.onOutput(message);
+        }
+
+        public void setDestination(StandardOutputListener destination) {
+            this.destination = destination;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultStyledTextOutputFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultStyledTextOutputFactory.java
new file mode 100644
index 0000000..40c902d
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/DefaultStyledTextOutputFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.util.TimeProvider;
+
+public class DefaultStyledTextOutputFactory extends AbstractStyledTextOutputFactory implements StyledTextOutputFactory {
+    private final OutputEventListener outputEventListener;
+    private final TimeProvider timeProvider;
+
+    public DefaultStyledTextOutputFactory(OutputEventListener outputEventListener, TimeProvider timeProvider) {
+        this.outputEventListener = outputEventListener;
+        this.timeProvider = timeProvider;
+    }
+
+    public StyledTextOutput create(String logCategory, LogLevel logLevel) {
+        return new LoggingBackedStyledTextOutput(outputEventListener, logCategory, logLevel, timeProvider);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurer.java
new file mode 100644
index 0000000..85b21fb
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.slf4j.bridge.SLF4JBridgeHandler;
+
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+/**
+ * A {@link org.gradle.logging.internal.LoggingConfigurer} which configures JUL to route logging events to SLF4J.
+ */
+public class JavaUtilLoggingConfigurer implements LoggingConfigurer {
+    private boolean configured;
+
+    public void configure(LogLevel logLevel) {
+        if (configured) {
+            return;
+        }
+
+        LogManager.getLogManager().reset();
+        SLF4JBridgeHandler.install();
+        Logger.getLogger("").setLevel(java.util.logging.Level.FINE);
+        configured = true;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Label.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Label.java
new file mode 100644
index 0000000..afc82fe
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Label.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+public interface Label {
+    void setText(String text);
+
+    void close();
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LogEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LogEvent.java
new file mode 100644
index 0000000..e0adc2f
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LogEvent.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+
+public class LogEvent extends RenderableOutputEvent {
+    private final String message;
+    private final Throwable throwable;
+
+    public LogEvent(long timestamp, String category, LogLevel logLevel, String message, Throwable throwable) {
+        super(timestamp, category, logLevel);
+        this.message = message;
+        this.throwable = throwable;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public Throwable getThrowable() {
+        return throwable;
+    }
+
+    public void render(StyledTextOutput output) {
+        output.text(message);
+        output.println();
+        if (throwable != null) {
+            output.exception(throwable);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("[%s] [%s] %s", getLogLevel(), getCategory(), message);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LogLevelChangeEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LogLevelChangeEvent.java
new file mode 100644
index 0000000..c0f574f
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LogLevelChangeEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+public class LogLevelChangeEvent extends OutputEvent {
+    private final LogLevel newLogLevel;
+
+    public LogLevelChangeEvent(LogLevel newLogLevel) {
+        this.newLogLevel = newLogLevel;
+    }
+
+    public LogLevel getNewLogLevel() {
+        return newLogLevel;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s %s", LogLevelChangeEvent.class.getSimpleName(), newLogLevel);
+    }
+
+    @Override
+    public LogLevel getLogLevel() {
+        return null;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingBackedStyledTextOutput.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingBackedStyledTextOutput.java
new file mode 100644
index 0000000..365285f
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingBackedStyledTextOutput.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.util.LineBufferingOutputStream;
+import org.gradle.util.TimeProvider;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link org.gradle.logging.StyledTextOutput} implementation which generates events of type {@link
+ * org.gradle.logging.internal.StyledTextOutputEvent}. This implementation is not thread-safe.
+ */
+public class LoggingBackedStyledTextOutput extends AbstractStyledTextOutput {
+    private final LineBufferingOutputStream outstr;
+    private Style style = Style.Normal;
+    private boolean styleChange;
+
+    public LoggingBackedStyledTextOutput(OutputEventListener listener, String category, LogLevel logLevel, TimeProvider timeProvider) {
+        outstr = new LineBufferingOutputStream(new LogAction(listener, category, logLevel, timeProvider), true);
+    }
+
+    protected void doStyleChange(Style style) {
+        styleChange = true;
+        try {
+            outstr.flush();
+        } finally {
+            styleChange = false;
+        }
+        this.style = style;
+    }
+
+    @Override
+    protected void doAppend(String text) {
+        try {
+            outstr.write(text.getBytes());
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private class LogAction implements Action<String> {
+        private final OutputEventListener listener;
+        private final String category;
+        private final LogLevel logLevel;
+        private final TimeProvider timeProvider;
+        private List<StyledTextOutputEvent.Span> spans;
+
+        public LogAction(OutputEventListener listener, String category, LogLevel logLevel, TimeProvider timeProvider) {
+            this.listener = listener;
+            this.category = category;
+            this.logLevel = logLevel;
+            this.timeProvider = timeProvider;
+        }
+
+        public void execute(String text) {
+            if (text.length() == 0) {
+                return;
+            }
+
+            StyledTextOutputEvent.Span span = new StyledTextOutputEvent.Span(style, text);
+            if (styleChange) {
+                if (spans == null) {
+                    spans = new ArrayList<StyledTextOutputEvent.Span>();
+                }
+                spans.add(span);
+                return;
+            } else if (spans != null) {
+                spans.add(span);
+            } else {
+                spans = Collections.singletonList(span);
+            }
+
+            StyledTextOutputEvent event = new StyledTextOutputEvent(timeProvider.getCurrentTime(), category, logLevel, spans);
+            spans = null;
+            
+            listener.onOutput(event);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java
new file mode 100644
index 0000000..fa6a29a
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingCommandLineConverter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import org.gradle.CommandLineArgumentException;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.initialization.AbstractCommandLineConverter;
+import org.gradle.initialization.CommandLineParser;
+import org.gradle.initialization.ParsedCommandLine;
+import org.gradle.logging.LoggingConfiguration;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class LoggingCommandLineConverter extends AbstractCommandLineConverter<LoggingConfiguration> {
+    public static final String DEBUG = "d";
+    public static final String INFO = "i";
+    public static final String QUIET = "q";
+    public static final String NO_COLOR = "no-color";
+    private final BiMap<String, LogLevel> logLevelMap = HashBiMap.create();
+
+    public LoggingCommandLineConverter() {
+        logLevelMap.put(QUIET, LogLevel.QUIET);
+        logLevelMap.put(INFO, LogLevel.INFO);
+        logLevelMap.put(DEBUG, LogLevel.DEBUG);
+        logLevelMap.put("", LogLevel.LIFECYCLE);
+    }
+
+    public LoggingConfiguration convert(ParsedCommandLine commandLine) throws CommandLineArgumentException {
+        LoggingConfiguration loggingConfiguration = new LoggingConfiguration();
+        loggingConfiguration.setLogLevel(getLogLevel(commandLine));
+        if (commandLine.hasOption(NO_COLOR)) {
+            loggingConfiguration.setColorOutput(false);
+        }
+        return loggingConfiguration;
+    }
+
+    private LogLevel getLogLevel(ParsedCommandLine options) {
+        LogLevel logLevel = LogLevel.LIFECYCLE;
+        if (options.hasOption(QUIET)) {
+            logLevel = LogLevel.QUIET;
+        }
+        if (options.hasOption(INFO)) {
+            logLevel = LogLevel.INFO;
+        }
+        if (options.hasOption(DEBUG)) {
+            logLevel = LogLevel.DEBUG;
+        }
+        return logLevel;
+    }
+
+    public void configure(CommandLineParser parser) {
+        parser.option(DEBUG, "debug").hasDescription("Log in debug mode (includes normal stacktrace).");
+        parser.option(QUIET, "quiet").hasDescription("Log errors only.");
+        parser.option(INFO, "info").hasDescription("Set log level to info.");
+        parser.option(NO_COLOR).hasDescription("Do not use color in the console output.");
+    }
+
+    /**
+     * This returns the log level object represented by the command line argument
+     *
+     * @param commandLineArgument a single command line argument (with no '-')
+     * @return the corresponding log level or null if it doesn't match any.
+     * @author mhunsicker
+     */
+    public LogLevel getLogLevel(String commandLineArgument) {
+        LogLevel logLevel = logLevelMap.get(commandLineArgument);
+        if (logLevel == null) {
+            return null;
+        }
+
+        return logLevel;
+    }
+
+    /**
+     * This returns the command line argument that represents the specified log level.
+     *
+     * @param logLevel the log level.
+     * @return the command line argument or null if this level cannot be represented on the command line.
+     * @author mhunsicker
+     */
+    public String getLogLevelCommandLine(LogLevel logLevel) {
+        String commandLine = logLevelMap.inverse().get(logLevel);
+        if (commandLine == null) {
+            return null;
+        }
+
+        return commandLine;
+    }
+
+    /**
+     * This returns the log levels that are supported on the command line.
+     *
+     * @return a collection of available log levels
+     * @author mhunsicker
+     */
+    public Collection<LogLevel> getLogLevels() {
+        return Collections.unmodifiableCollection(logLevelMap.values());
+    }
+
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java
new file mode 100644
index 0000000..6553da8
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingConfigurer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+/**
+ * @author Hans Dockter
+ */
+public interface LoggingConfigurer {
+    void configure(LogLevel logLevel);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java
new file mode 100644
index 0000000..e2c91f2
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingOutputInternal.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LoggingOutput;
+
+public interface LoggingOutputInternal extends LoggingOutput {
+    void colorStdOutAndStdErr(boolean colorOutput);
+
+    void addOutputEventListener(OutputEventListener listener);
+
+    void removeOutputEventListener(OutputEventListener listener);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingSystem.java
new file mode 100644
index 0000000..68b0408
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingSystem.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+public interface LoggingSystem {
+    Snapshot snapshot();
+
+    /**
+     * Enables logging for this logging system at the given level.
+     *
+     * @param level The new level.
+     * @return the state of this logging system immediately before the changes are applied.
+     */
+    Snapshot on(LogLevel level);
+
+    /**
+     * Disables logging for this logging system
+     *
+     * @return the state of this logging system immediately before the changes are applied.
+     */
+    Snapshot off();
+
+    void restore(Snapshot state);
+
+    interface Snapshot {
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingSystemAdapter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingSystemAdapter.java
new file mode 100644
index 0000000..613f73d
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/LoggingSystemAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+/**
+ * Adapts a {@link LoggingConfigurer} to a {@link LoggingSystem}.
+ */
+public class LoggingSystemAdapter implements LoggingSystem {
+    private final LoggingConfigurer configurer;
+    private LogLevel logLevel = LogLevel.LIFECYCLE;
+
+    public LoggingSystemAdapter(LoggingConfigurer configurer) {
+        this.configurer = configurer;
+    }
+
+    public Snapshot snapshot() {
+        return new SnapshotImpl(logLevel);
+    }
+
+    public Snapshot off() {
+        return new SnapshotImpl(logLevel);
+    }
+
+    public Snapshot on(LogLevel level) {
+        SnapshotImpl snapshot = new SnapshotImpl(logLevel);
+        setLevel(level);
+        return snapshot;
+    }
+
+    public void restore(Snapshot state) {
+        LogLevel oldLevel = ((SnapshotImpl) state).level;
+        this.logLevel = oldLevel;
+        if (oldLevel != null) {
+            configurer.configure(oldLevel);
+        }
+    }
+
+    private void setLevel(LogLevel level) {
+        configurer.configure(level);
+        this.logLevel = level;
+    }
+
+    private class SnapshotImpl implements Snapshot {
+        private final LogLevel level;
+
+        public SnapshotImpl(LogLevel level) {
+            this.level = level;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/MarkerFilter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/MarkerFilter.java
new file mode 100644
index 0000000..93d014c
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/MarkerFilter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterReply;
+import org.slf4j.Marker;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Hans Dockter
+ */
+public class MarkerFilter extends Filter<ILoggingEvent> {
+    private final List<Marker> markers;
+
+    private FilterReply onMismatch = FilterReply.NEUTRAL;
+
+    public MarkerFilter(Marker... markers) {
+        this.markers = Arrays.asList(markers);
+    }
+
+    public MarkerFilter(FilterReply onMismatch, Marker... markers) {
+        this(markers);
+        this.onMismatch = onMismatch;
+    }
+
+    @Override
+    public FilterReply decide(ILoggingEvent loggingEvent) {
+        Marker marker = loggingEvent.getMarker();
+        if (marker != null) {
+            for (Marker candidate : markers) {
+                if (marker.contains(candidate)) {
+                    return FilterReply.ACCEPT;
+                }
+            }
+        }
+        return onMismatch;
+    }
+
+    public FilterReply getOnMismatch() {
+        return onMismatch;
+    }
+
+    public void setOnMismatch(FilterReply onMismatch) {
+        this.onMismatch = onMismatch;
+    }
+
+    public List getMarkers() {
+        return markers;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEvent.java
new file mode 100644
index 0000000..288f0bc
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+import java.io.Serializable;
+
+/**
+ * Represents some event which may generate output. All implementations are immutable.
+ */
+public abstract class OutputEvent implements Serializable {
+    /**
+     * Returns the log level for this event. May return null.
+     */
+    public abstract LogLevel getLogLevel();
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEventListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEventListener.java
new file mode 100644
index 0000000..cefcff4
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEventListener.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+public interface OutputEventListener {
+    void onOutput(OutputEvent event);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
new file mode 100644
index 0000000..83d9d07
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/OutputEventRenderer.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.listener.ListenerBroadcast;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintStream;
+
+/**
+ * A {@link org.gradle.logging.internal.OutputEventListener} implementation which renders output events to various
+ * destinations. This implementation is thread-safe.
+ */
+public class OutputEventRenderer implements OutputEventListener, LoggingConfigurer, LoggingOutputInternal {
+    private final ListenerBroadcast<OutputEventListener> formatters = new ListenerBroadcast<OutputEventListener>(OutputEventListener.class);
+    private final ListenerBroadcast<StandardOutputListener> stdoutListeners = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
+    private final ListenerBroadcast<StandardOutputListener> stderrListeners = new ListenerBroadcast<StandardOutputListener>(StandardOutputListener.class);
+    private final Object lock = new Object();
+    private final DefaultColorMap colourMap = new DefaultColorMap();
+    private LogLevel logLevel = LogLevel.LIFECYCLE;
+
+    public OutputEventRenderer() {
+        OutputEventListener stdOutChain = onNonError(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(new StreamingStyledTextOutput(stdoutListeners.getSource())), false));
+        formatters.add(stdOutChain);
+        OutputEventListener stdErrChain = onError(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(new StreamingStyledTextOutput(stderrListeners.getSource())), false));
+        formatters.add(stdErrChain);
+    }
+
+    public void colorStdOutAndStdErr(boolean colorOutput) {
+        synchronized (lock) {
+            colourMap.setUseColor(colorOutput);
+        }
+    }
+
+    public OutputEventRenderer addStandardOutputAndError() {
+        TerminalDetector terminalDetector = new TerminalDetector();
+        boolean stdOutIsTerminal = terminalDetector.isSatisfiedBy(FileDescriptor.out);
+        boolean stdErrIsTerminal = terminalDetector.isSatisfiedBy(FileDescriptor.err);
+        if (stdOutIsTerminal) {
+            PrintStream outStr = org.fusesource.jansi.AnsiConsole.out();
+            Console console = new AnsiConsole(outStr, outStr, colourMap);
+            addConsole(console, true, stdErrIsTerminal);
+        } else if (stdErrIsTerminal) {
+            // Only stderr is connected to a terminal
+            PrintStream errStr = org.fusesource.jansi.AnsiConsole.err();
+            Console console = new AnsiConsole(errStr, errStr, colourMap);
+            addConsole(console, false, true);
+        }
+        if (!stdOutIsTerminal) {
+            addStandardOutput(System.out);
+        }
+        if (!stdErrIsTerminal) {
+            addStandardError(System.err);
+        }
+        return this;
+    }
+
+    public OutputEventRenderer addStandardOutput(final Appendable out) {
+        addStandardOutputListener(new StandardOutputListener() {
+            public void onOutput(CharSequence output) {
+                try {
+                    out.append(output);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        });
+        return this;
+    }
+
+    public OutputEventRenderer addStandardError(final Appendable err) {
+        addStandardErrorListener(new StandardOutputListener() {
+            public void onOutput(CharSequence output) {
+                try {
+                    err.append(output);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        });
+        return this;
+    }
+
+    public void addOutputEventListener(OutputEventListener listener) {
+        formatters.add(listener);
+    }
+
+    public void removeOutputEventListener(OutputEventListener listener) {
+        formatters.remove(listener);
+    }
+
+    public OutputEventRenderer addConsole(final Console console, boolean stdout, boolean stderr) {
+        final OutputEventListener consoleChain = new ConsoleBackedProgressRenderer(new ProgressLogEventGenerator(new StyledTextOutputBackedRenderer(console.getMainArea()), true), console);
+        synchronized (lock) {
+            if (stdout && stderr) {
+                formatters.add(consoleChain);
+            } else if (stdout) {
+                formatters.add(onNonError(consoleChain));
+            } else {
+                formatters.add(onError(consoleChain));
+            }
+        }
+        return this;
+    }
+
+    private OutputEventListener onError(final OutputEventListener listener) {
+        return new OutputEventListener() {
+            public void onOutput(OutputEvent event) {
+                if (event.getLogLevel() == LogLevel.ERROR || event.getLogLevel() == null) {
+                    listener.onOutput(event);
+                }
+            }
+        };
+    }
+
+    private OutputEventListener onNonError(final OutputEventListener listener) {
+        return new OutputEventListener() {
+            public void onOutput(OutputEvent event) {
+                if (event.getLogLevel() != LogLevel.ERROR || event.getLogLevel() == null) {
+                    listener.onOutput(event);
+                }
+            }
+        };
+    }
+
+    public void addStandardErrorListener(StandardOutputListener listener) {
+        synchronized (lock) {
+            stderrListeners.add(listener);
+        }
+    }
+
+    public void addStandardOutputListener(StandardOutputListener listener) {
+        synchronized (lock) {
+            stdoutListeners.add(listener);
+        }
+    }
+
+    public void removeStandardOutputListener(StandardOutputListener listener) {
+        synchronized (lock) {
+            stdoutListeners.remove(listener);
+        }
+    }
+
+    public void removeStandardErrorListener(StandardOutputListener listener) {
+        synchronized (lock) {
+            stderrListeners.remove(listener);
+        }
+    }
+
+    public void configure(LogLevel logLevel) {
+        onOutput(new LogLevelChangeEvent(logLevel));
+    }
+
+    public void onOutput(OutputEvent event) {
+        synchronized (lock) {
+            if (event.getLogLevel() != null && event.getLogLevel().compareTo(logLevel) < 0) {
+                return;
+            }
+            if (event instanceof LogLevelChangeEvent) {
+                LogLevelChangeEvent changeEvent = (LogLevelChangeEvent) event;
+                LogLevel newLogLevel = changeEvent.getNewLogLevel();
+                if (newLogLevel == this.logLevel) {
+                    return;
+                }
+                this.logLevel = newLogLevel;
+            }
+            formatters.getSource().onOutput(event);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/PrintStreamLoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/PrintStreamLoggingSystem.java
new file mode 100644
index 0000000..1fb684a
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/PrintStreamLoggingSystem.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.Action;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.util.LinePerThreadBufferingOutputStream;
+import org.gradle.util.TimeProvider;
+
+import java.io.PrintStream;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A {@link LoggingSystem} which routes content written to a {@code PrintStream} to a {@link OutputEventListener}.
+ * Generates a {@link StyledTextOutputEvent} instance when a line of text is written to the {@code PrintStream}.
+ * Generates a {@link LogLevelChangeEvent} when the log level for this {@code LoggingSystem} is changed.
+ */
+abstract class PrintStreamLoggingSystem implements LoggingSystem {
+    private final AtomicReference<StandardOutputListener> destination = new AtomicReference<StandardOutputListener>();
+    private final PrintStream outstr = new LinePerThreadBufferingOutputStream(new Action<String>() {
+        public void execute(String output) {
+            destination.get().onOutput(output);
+        }
+    }, true);
+    private StandardOutputListener original;
+    private LogLevel logLevel;
+    private final StandardOutputListener listener;
+    private final OutputEventListener outputEventListener;
+
+    protected PrintStreamLoggingSystem(OutputEventListener listener, String category, TimeProvider timeProvider) {
+        outputEventListener = listener;
+        this.listener = new OutputEventDestination(listener, category, timeProvider);
+    }
+
+    /**
+     * Returns the current value of the PrintStream
+     */
+    protected abstract PrintStream get();
+
+    /**
+     * Sets the current value of the PrintStream
+     */
+    protected abstract void set(PrintStream printStream);
+
+    public Snapshot snapshot() {
+        return new SnapshotImpl(logLevel);
+    }
+
+    public void restore(Snapshot state) {
+        SnapshotImpl snapshot = (SnapshotImpl) state;
+        install();
+        if (snapshot.logLevel == null) {
+            destination.set(original);
+        } else {
+            this.logLevel = snapshot.logLevel;
+            outputEventListener.onOutput(new LogLevelChangeEvent(snapshot.logLevel));
+            destination.set(listener);
+        }
+    }
+
+    public Snapshot on(final LogLevel level) {
+        Snapshot snapshot = snapshot();
+        install();
+        this.logLevel = level;
+        outputEventListener.onOutput(new LogLevelChangeEvent(logLevel));
+        destination.set(listener);
+        return snapshot;
+    }
+
+    public Snapshot off() {
+        Snapshot snapshot = snapshot();
+        if (original != null) {
+            outstr.flush();
+            destination.set(original);
+            logLevel = null;
+        }
+        return snapshot;
+    }
+
+    private void install() {
+        if (original == null) {
+            PrintStream originalStream = get();
+            original = new PrintStreamDestination(originalStream);
+        }
+        outstr.flush();
+        if (get() != outstr) {
+            set(outstr);
+        }
+    }
+
+    private static class PrintStreamDestination implements StandardOutputListener {
+        private final PrintStream originalStream;
+
+        public PrintStreamDestination(PrintStream originalStream) {
+            this.originalStream = originalStream;
+        }
+
+        public void onOutput(CharSequence output) {
+            originalStream.print(output);
+        }
+    }
+
+    private static class SnapshotImpl implements Snapshot {
+        private final LogLevel logLevel;
+
+        public SnapshotImpl(LogLevel logLevel) {
+            this.logLevel = logLevel;
+        }
+    }
+
+    private static class OutputEventDestination implements StandardOutputListener {
+        private final OutputEventListener listener;
+        private final String category;
+        private final TimeProvider timeProvider;
+
+        public OutputEventDestination(OutputEventListener listener, String category, TimeProvider timeProvider) {
+            this.listener = listener;
+            this.category = category;
+            this.timeProvider = timeProvider;
+        }
+
+        public void onOutput(CharSequence output) {
+            listener.onOutput(new StyledTextOutputEvent(timeProvider.getCurrentTime(), category, output.toString()));
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressCompleteEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressCompleteEvent.java
new file mode 100644
index 0000000..a8082fd
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressCompleteEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+public class ProgressCompleteEvent extends CategorisedOutputEvent {
+    private final String status;
+
+    public ProgressCompleteEvent(long timestamp, String category, String status) {
+        super(timestamp, category, LogLevel.LIFECYCLE);
+        this.status = status;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ProgressComplete %s", status);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressEvent.java
new file mode 100644
index 0000000..db04582
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+public class ProgressEvent extends CategorisedOutputEvent {
+    private final String status;
+
+    public ProgressEvent(long timestamp, String category, String status) {
+        super(timestamp, category, LogLevel.LIFECYCLE);
+        this.status = status;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Progress %s", status);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressListener.java
new file mode 100644
index 0000000..fc7600e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressListener.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+public interface ProgressListener {
+    void started(ProgressStartEvent event);
+
+    void progress(ProgressEvent event);
+
+    void completed(ProgressCompleteEvent event);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
new file mode 100644
index 0000000..9d04cd5
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressLogEventGenerator.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+import java.util.LinkedList;
+
+import static org.gradle.logging.StyledTextOutput.Style;
+
+/**
+ * An {@code org.gradle.logging.internal.OutputEventListener} implementation which generates output events to log the
+ * progress of operations.
+ */
+public class ProgressLogEventGenerator implements OutputEventListener {
+    public static final String EOL = System.getProperty("line.separator");
+    private final OutputEventListener listener;
+    private final boolean deferHeader;
+    private final LinkedList<Operation> operations = new LinkedList<Operation>();
+
+    public ProgressLogEventGenerator(OutputEventListener listener, boolean deferHeader) {
+        this.listener = listener;
+        this.deferHeader = deferHeader;
+    }
+
+    public void onOutput(OutputEvent event) {
+        if (event instanceof ProgressStartEvent) {
+            onStart((ProgressStartEvent) event);
+        } else if (event instanceof ProgressCompleteEvent) {
+            onComplete((ProgressCompleteEvent) event);
+        } else if (event instanceof RenderableOutputEvent) {
+            doOutput((RenderableOutputEvent) event);
+        } else if (!(event instanceof ProgressEvent)) {
+            listener.onOutput(event);
+        }
+    }
+
+    private void doOutput(RenderableOutputEvent event) {
+        for (Operation operation : operations) {
+            operation.completeHeader();
+        }
+        listener.onOutput(event);
+    }
+
+    private void onComplete(ProgressCompleteEvent progressCompleteEvent) {
+        assert !operations.isEmpty();
+        Operation operation = operations.removeLast();
+        operation.status = progressCompleteEvent.getStatus();
+        operation.completeTime = progressCompleteEvent.getTimestamp();
+        operation.complete();
+    }
+
+    private void onStart(ProgressStartEvent progressStartEvent) {
+        Operation operation = new Operation();
+        operation.category = progressStartEvent.getCategory();
+        operation.description = progressStartEvent.getDescription();
+        operation.startTime = progressStartEvent.getTimestamp();
+        operation.status = "";
+        operations.add(operation);
+
+        if (!deferHeader) {
+            operation.startHeader();
+        }
+    }
+
+    enum State {None, HeaderStarted, HeaderCompleted, Completed}
+
+    private class Operation {
+        private String category;
+        private String description;
+        private String status;
+        private State state = State.None;
+        public long startTime;
+        public long completeTime;
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+
+        private void doOutput(RenderableOutputEvent event) {
+            for (Operation pending : operations) {
+                if (pending == this) {
+                    break;
+                }
+                pending.completeHeader();
+            }
+            listener.onOutput(event);
+        }
+
+        public void startHeader() {
+            assert state == State.None;
+            boolean hasDescription = description.length() > 0;
+            if (hasDescription) {
+                state = State.HeaderStarted;
+                doOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, description));
+            } else {
+                state = State.HeaderCompleted;
+            }
+        }
+
+        public void completeHeader() {
+            boolean hasDescription = description.length() > 0;
+            switch (state) {
+                case None:
+                    if (hasDescription) {
+                        listener.onOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, description + EOL));
+                    }
+                    break;
+                case HeaderStarted:
+                    listener.onOutput(new StyledTextOutputEvent(startTime, category, LogLevel.LIFECYCLE, EOL));
+                    break;
+                case HeaderCompleted:
+                    return;
+                default:
+                    throw new IllegalStateException();
+            }
+            state = State.HeaderCompleted;
+        }
+
+        public void complete() {
+            boolean hasStatus = status.length() > 0;
+            boolean hasDescription = description.length() > 0;
+            switch (state) {
+                case None:
+                    if (hasDescription && hasStatus) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE,
+                                new StyledTextOutputEvent.Span(description + ' '),
+                                new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
+                                new StyledTextOutputEvent.Span(EOL)));
+                    } else if (hasDescription) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE, description + EOL));
+                    } else if (hasStatus) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE,
+                                new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
+                                new StyledTextOutputEvent.Span(EOL)));
+                    }
+                    break;
+                case HeaderStarted:
+                    if (hasStatus) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE,
+                                new StyledTextOutputEvent.Span(" "),
+                                new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
+                                new StyledTextOutputEvent.Span(EOL)));
+                    } else {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE, EOL));
+                    }
+                    break;
+                case HeaderCompleted:
+                    if (hasDescription && hasStatus) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE,
+                                new StyledTextOutputEvent.Span(description + ' '),
+                                new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
+                                new StyledTextOutputEvent.Span(EOL)));
+                    } else if (hasStatus) {
+                        doOutput(new StyledTextOutputEvent(completeTime, category, LogLevel.LIFECYCLE, 
+                                new StyledTextOutputEvent.Span(Style.ProgressStatus, status),
+                                new StyledTextOutputEvent.Span(EOL)));
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException();
+            }
+            state = State.Completed;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressLoggingBridge.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressLoggingBridge.java
new file mode 100644
index 0000000..a352abd
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressLoggingBridge.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+public class ProgressLoggingBridge implements ProgressListener {
+    private final OutputEventListener listener;
+
+    public ProgressLoggingBridge(OutputEventListener listener) {
+        this.listener = listener;
+    }
+
+    public void completed(ProgressCompleteEvent event) {
+        listener.onOutput(event);
+    }
+
+    public void started(ProgressStartEvent event) {
+        listener.onOutput(event);
+    }
+
+    public void progress(ProgressEvent event) {
+        listener.onOutput(event);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java
new file mode 100644
index 0000000..86d7f1c
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/ProgressStartEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+public class ProgressStartEvent extends CategorisedOutputEvent {
+    private final String description;
+
+    public ProgressStartEvent(long timestamp, String category, String description) {
+        super(timestamp, category, LogLevel.LIFECYCLE);
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ProgressStart %s", description);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/RenderableOutputEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/RenderableOutputEvent.java
new file mode 100644
index 0000000..89b4b7a
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/RenderableOutputEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+
+public abstract class RenderableOutputEvent extends CategorisedOutputEvent {
+
+    protected RenderableOutputEvent(long timestamp, String category, LogLevel logLevel) {
+        super(timestamp, category, logLevel);
+    }
+
+    /**
+     * Renders this event to the given output. The output's style will be set to {@link
+     * org.gradle.logging.StyledTextOutput.Style#Normal}. The style will be reset after the rendering is complete, so
+     * there is no need for this method to clean up the style.
+     *
+     * @param output The output to render to.
+     */
+    public abstract void render(StyledTextOutput output);
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Slf4jLoggingConfigurer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Slf4jLoggingConfigurer.java
new file mode 100644
index 0000000..97bc3dc
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/Slf4jLoggingConfigurer.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.filter.LevelFilter;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.ThrowableProxy;
+import ch.qos.logback.core.AppenderBase;
+import ch.qos.logback.core.ConsoleAppender;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterReply;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logging;
+import org.gradle.util.UncheckedException;
+import org.slf4j.LoggerFactory;
+
+import java.io.PrintStream;
+
+/**
+ * A {@link org.gradle.logging.internal.LoggingConfigurer} implementation which configures SLF4J to route logging
+ * events to a {@link org.gradle.logging.internal.OutputEventListener}.
+ *
+ * @author Hans Dockter
+ */
+public class Slf4jLoggingConfigurer implements LoggingConfigurer {
+    private final Appender appender;
+    private LogLevel currentLevel;
+    private final PrintStream defaultStdOut;
+
+    public Slf4jLoggingConfigurer(OutputEventListener outputListener) {
+        defaultStdOut = System.out;
+        appender = new Appender(outputListener);
+    }
+
+    public void configure(LogLevel logLevel) {
+        if (currentLevel == logLevel) {
+            return;
+        }
+
+        try {
+            doConfigure(logLevel);
+        } catch (Throwable e) {
+            doFailsafeConfiguration();
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    private void doFailsafeConfiguration() {
+        // Not really failsafe, just less likely to fail
+        final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        lc.reset();
+
+        ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<ILoggingEvent>() {{
+            setContext(lc);
+            setTarget("System.err");
+            setLayout(new PatternLayout() {{
+                setPattern("%msg%n%ex");
+                setContext(lc);
+                start();
+            }});
+            start();
+        }};
+
+        ch.qos.logback.classic.Logger rootLogger = lc.getLogger("ROOT");
+        rootLogger.setLevel(Level.INFO);
+        rootLogger.addAppender(consoleAppender);
+    }
+
+    private void doConfigure(LogLevel logLevel) {
+        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        ch.qos.logback.classic.Logger rootLogger;
+        if (currentLevel == null) {
+            lc.reset();
+            appender.setContext(lc);
+            rootLogger = lc.getLogger("ROOT");
+            rootLogger.addAppender(appender);
+        } else {
+            rootLogger = lc.getLogger("ROOT");
+        }
+
+        currentLevel = logLevel;
+        appender.stop();
+        appender.clearAllFilters();
+
+        switch (logLevel) {
+            case DEBUG:
+                rootLogger.setLevel(Level.DEBUG);
+                break;
+            case INFO:
+                rootLogger.setLevel(Level.INFO);
+                break;
+            case LIFECYCLE:
+                appender.addFilter(new MarkerFilter(Logging.QUIET, Logging.LIFECYCLE));
+                appender.addFilter(createLevelFilter(lc, Level.INFO, FilterReply.DENY, FilterReply.NEUTRAL));
+                rootLogger.setLevel(Level.INFO);
+                break;
+            case QUIET:
+                appender.addFilter(new MarkerFilter(Logging.QUIET));
+                appender.addFilter(createLevelFilter(lc, Level.INFO, FilterReply.DENY, FilterReply.NEUTRAL));
+                rootLogger.setLevel(Level.INFO);
+                break;
+            case WARN:
+                rootLogger.setLevel(Level.WARN);
+                break;
+            case ERROR:
+                rootLogger.setLevel(Level.ERROR);
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+
+        appender.start();
+    }
+
+    private Filter<ILoggingEvent> createLevelFilter(LoggerContext lc, Level level, FilterReply onMatch,
+                                                    FilterReply onMismatch) {
+        LevelFilter levelFilter = new LevelFilter();
+        levelFilter.setContext(lc);
+        levelFilter.setOnMatch(onMatch);
+        levelFilter.setOnMismatch(onMismatch);
+        levelFilter.setLevel(level);
+        levelFilter.start();
+        return levelFilter;
+    }
+
+    private class Appender extends AppenderBase<ILoggingEvent> {
+        private final OutputEventListener listener;
+
+        private Appender(OutputEventListener listener) {
+            this.listener = listener;
+        }
+
+        @Override
+        protected void append(ILoggingEvent event) {
+            try {
+                ThrowableProxy throwableProxy = (ThrowableProxy) event.getThrowableProxy();
+                Throwable throwable = throwableProxy == null ? null : throwableProxy.getThrowable();
+                String message = event.getFormattedMessage();
+                listener.onOutput(new LogEvent(event.getTimeStamp(), event.getLoggerName(), toLogLevel(event), message, throwable));
+            } catch (Throwable t) {
+                // Give up and try stdout
+                t.printStackTrace(defaultStdOut);
+            }
+        }
+
+        private LogLevel toLogLevel(ILoggingEvent event) {
+            switch (event.getLevel().toInt()) {
+                case Level.DEBUG_INT:
+                    return LogLevel.DEBUG;
+                case Level.INFO_INT:
+                    if (event.getMarker() == Logging.LIFECYCLE) {
+                        return LogLevel.LIFECYCLE;
+                    }
+                    if (event.getMarker() == Logging.QUIET) {
+                        return LogLevel.QUIET;
+                    }
+                    return LogLevel.INFO;
+                case Level.WARN_INT:
+                    return LogLevel.WARN;
+                case Level.ERROR_INT:
+                    return LogLevel.ERROR;
+            }
+            throw new IllegalArgumentException(String.format("Cannot map SLF4j Level %s to a LogLevel", event.getLevel()));
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java
new file mode 100644
index 0000000..43ad966
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StdErrLoggingSystem.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.util.TimeProvider;
+
+import java.io.PrintStream;
+
+public class StdErrLoggingSystem extends PrintStreamLoggingSystem {
+    public StdErrLoggingSystem(OutputEventListener listener, TimeProvider timeProvider) {
+        super(listener, "system.err", timeProvider);
+    }
+
+    @Override
+    protected PrintStream get() {
+        return System.err;
+    }
+
+    @Override
+    protected void set(PrintStream printStream) {
+        System.setErr(printStream);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java
new file mode 100644
index 0000000..09d395d
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StdOutLoggingSystem.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.util.TimeProvider;
+
+import java.io.PrintStream;
+
+public class StdOutLoggingSystem extends PrintStreamLoggingSystem {
+    public StdOutLoggingSystem(OutputEventListener listener, TimeProvider timeProvider) {
+        super(listener, "system.out", timeProvider);
+    }
+
+    @Override
+    protected PrintStream get() {
+        return System.out;
+    }
+
+    @Override
+    protected void set(PrintStream printStream) {
+        System.setOut(printStream);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java
new file mode 100644
index 0000000..aa83214
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutput.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.logging.StandardOutputListener;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * A {@link org.gradle.logging.StyledTextOutput} implementation which writes text to some char stream. Ignores any
+ * styling information.
+ */
+public class StreamingStyledTextOutput extends AbstractStyledTextOutput implements Closeable {
+    private final StandardOutputListener listener;
+    private final Closeable closeable;
+
+    /**
+     * Creates a text output which forwards text to the given listener.
+     * @param listener The listener.
+     */
+    public StreamingStyledTextOutput(StandardOutputListener listener) {
+        this(listener, listener);
+    }
+
+    /**
+     * Creates a text output which writes text to the given appendable.
+     * @param appendable The appendable.
+     */
+    public StreamingStyledTextOutput(final Appendable appendable) {
+        this(appendable, new StandardOutputListener() {
+            public void onOutput(CharSequence output) {
+                try {
+                    appendable.append(output);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            }
+        });
+    }
+
+    private StreamingStyledTextOutput(Object target, StandardOutputListener listener) {
+        this.listener = listener;
+        closeable = target instanceof Closeable ? (Closeable) target : null;
+    }
+
+    @Override
+    protected void doAppend(String text) {
+        listener.onOutput(text);
+    }
+
+    /**
+     * Closes the target object if it implements {@link java.io.Closeable}.
+     */
+    public void close() throws IOException {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutputFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutputFactory.java
new file mode 100644
index 0000000..cfddb58
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StreamingStyledTextOutputFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+
+public class StreamingStyledTextOutputFactory extends AbstractStyledTextOutputFactory {
+    private final Appendable target;
+
+    public StreamingStyledTextOutputFactory(Appendable target) {
+        this.target = target;
+    }
+
+    public StyledTextOutput create(String logCategory, LogLevel logLevel) {
+        return new StreamingStyledTextOutput(target);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java
new file mode 100644
index 0000000..49697e0
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputBackedRenderer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.gradle.logging.StyledTextOutput.Style.Error;
+import static org.gradle.logging.StyledTextOutput.Style.Normal;
+
+public class StyledTextOutputBackedRenderer implements OutputEventListener {
+    private final static String EOL = System.getProperty("line.separator");
+    private final OutputEventTextOutputImpl textOutput;
+    private boolean debugOutput;
+    private RenderableOutputEvent lastEvent;
+
+    public StyledTextOutputBackedRenderer(StyledTextOutput textOutput) {
+        this.textOutput = new OutputEventTextOutputImpl(textOutput);
+    }
+
+    public void onOutput(OutputEvent event) {
+        if (event instanceof LogLevelChangeEvent) {
+            LogLevelChangeEvent changeEvent = (LogLevelChangeEvent) event;
+            debugOutput = changeEvent.getNewLogLevel() == LogLevel.DEBUG;
+        }
+        if (event instanceof RenderableOutputEvent) {
+            RenderableOutputEvent outputEvent = (RenderableOutputEvent) event;
+            textOutput.style(outputEvent.getLogLevel() == LogLevel.ERROR ? Error : Normal);
+            if (debugOutput && (textOutput.atEndOfLine || lastEvent == null || !lastEvent.getCategory().equals(outputEvent.getCategory()))) {
+                if (!textOutput.atEndOfLine) {
+                    textOutput.println();
+                }
+                textOutput.text(new SimpleDateFormat("HH:mm:ss.SSS").format(new Date(outputEvent.getTimestamp())));
+                textOutput.text(" [");
+                textOutput.text(outputEvent.getLogLevel());
+                textOutput.text("] [");
+                textOutput.text(outputEvent.getCategory());
+                textOutput.text("] ");
+            }
+            outputEvent.render(textOutput);
+            lastEvent = outputEvent;
+            textOutput.style(Normal);
+        }
+    }
+
+    private class OutputEventTextOutputImpl extends AbstractStyledTextOutput {
+        private final StyledTextOutput textOutput;
+        private boolean atEndOfLine = true;
+
+        public OutputEventTextOutputImpl(StyledTextOutput textOutput) {
+            this.textOutput = textOutput;
+        }
+
+        @Override
+        protected void doStyleChange(Style style) {
+            textOutput.style(style);
+        }
+
+        @Override
+        protected void doAppend(String text) {
+            atEndOfLine = text.length() >= EOL.length() && text.endsWith(EOL);
+            textOutput.text(text);
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputEvent.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputEvent.java
new file mode 100644
index 0000000..14b6998
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/StyledTextOutputEvent.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutput;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.gradle.logging.StyledTextOutput.Style.*;
+
+public class StyledTextOutputEvent extends RenderableOutputEvent {
+    private final List<Span> spans;
+
+    public StyledTextOutputEvent(long timestamp, String category, String text) {
+        this(timestamp, category, Normal, text);
+    }
+
+    public StyledTextOutputEvent(long timestamp, String category, LogLevel logLevel, String text) {
+        this(timestamp, category, logLevel, Normal, text);
+    }
+
+    public StyledTextOutputEvent(long timestamp, String category, StyledTextOutput.Style style, String text) {
+        this(timestamp, category, null, style, text);
+    }
+
+    public StyledTextOutputEvent(long timestamp, String category, LogLevel logLevel, StyledTextOutput.Style style, String text) {
+        this(timestamp, category, logLevel, Collections.singletonList(new Span(style, text)));
+    }
+
+    public StyledTextOutputEvent(long timestamp, String category, List<Span> spans) {
+        this(timestamp, category, null, spans);
+    }
+
+    public StyledTextOutputEvent(long timestamp, String category, LogLevel logLevel, Span... spans) {
+        this(timestamp, category, logLevel, Arrays.asList(spans));
+    }
+
+    public StyledTextOutputEvent(long timestamp, String category, LogLevel logLevel, List<Span> spans) {
+        super(timestamp, category, logLevel);
+        this.spans = new ArrayList<Span>(spans);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append('[').append(getLogLevel()).append("] [");
+        builder.append(getCategory()).append("] ");
+        for (Span span : spans) {
+            builder.append('<');
+            builder.append(span.style);
+            builder.append(">");
+            builder.append(span.text);
+            builder.append("</");
+            builder.append(span.style);
+            builder.append(">");
+        }
+        return builder.toString();
+    }
+
+    public StyledTextOutputEvent withLogLevel(LogLevel logLevel) {
+        return new StyledTextOutputEvent(getTimestamp(), getCategory(), logLevel, spans);
+    }
+
+    @Override
+    public void render(StyledTextOutput output) {
+        for (Span span : spans) {
+            output.style(span.style);
+            output.text(span.text);
+        }
+    }
+
+    public static class Span implements Serializable {
+        private final String text;
+        private final StyledTextOutput.Style style;
+
+        public Span(StyledTextOutput.Style style, String text) {
+            this.style = style;
+            this.text = text;
+        }
+
+        public Span(String text) {
+            this.style = Normal;
+            this.text = text;
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java
new file mode 100644
index 0000000..2c1c8ba
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TerminalDetector.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.fusesource.jansi.WindowsAnsiOutputStream;
+import org.gradle.api.specs.Spec;
+import org.gradle.util.OperatingSystem;
+import org.gradle.util.PosixUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class TerminalDetector implements Spec<FileDescriptor> {
+    public boolean isSatisfiedBy(FileDescriptor element) {
+        if (OperatingSystem.current().isWindows()) {
+            // Use Jansi's detection mechanism
+            try {
+                new WindowsAnsiOutputStream(new ByteArrayOutputStream());
+            } catch (IOException ignore) {
+                // Not attached to a console
+                return false;
+            }
+        }
+
+        // Use jna-posix to determine if we're connected to a terminal
+        if (!PosixUtil.current().isatty(element)) {
+            return false;
+        }
+
+        // Dumb terminal doesn't support control codes. Should really be using termcap database.
+        String term = System.getenv("TERM");
+        if (term != null && term.equals("dumb")) {
+            return false;
+        }
+
+        // Assume a terminal
+        return true;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TextArea.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TextArea.java
new file mode 100644
index 0000000..8e3a272
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TextArea.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal;
+
+import org.gradle.logging.StyledTextOutput;
+
+public interface TextArea extends StyledTextOutput {
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TextStreamOutputEventListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TextStreamOutputEventListener.java
new file mode 100644
index 0000000..355cb4e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/logging/internal/TextStreamOutputEventListener.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A {@link org.gradle.logging.internal.OutputEventListener} implementation which assigns log levels to text output
+ * events that have no associated log level. This implementation is thread-safe.
+ */
+public class TextStreamOutputEventListener implements OutputEventListener {
+    private final OutputEventListener listener;
+    private AtomicReference<LogLevel> logLevel = new AtomicReference<LogLevel>(LogLevel.LIFECYCLE);
+
+    public TextStreamOutputEventListener(OutputEventListener listener) {
+        this.listener = listener;
+    }
+
+    public void onOutput(OutputEvent event) {
+        if (event instanceof StyledTextOutputEvent) {
+            onTextEvent((StyledTextOutputEvent) event);
+        } else if (event instanceof LogLevelChangeEvent) {
+            onLogLevelChange((LogLevelChangeEvent) event);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private void onLogLevelChange(LogLevelChangeEvent changeEvent) {
+        logLevel.set(changeEvent.getNewLogLevel());
+    }
+
+    private void onTextEvent(StyledTextOutputEvent textOutputEvent) {
+        if (textOutputEvent.getLogLevel() != null) {
+            listener.onOutput(textOutputEvent);
+        } else {
+            listener.onOutput(textOutputEvent.withLogLevel(logLevel.get()));
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/messaging/remote/internal/Message.java b/subprojects/gradle-core/src/main/groovy/org/gradle/messaging/remote/internal/Message.java
index d938d9c..d0dbf9a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/messaging/remote/internal/Message.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/messaging/remote/internal/Message.java
@@ -45,9 +45,21 @@ public abstract class Message implements Serializable {
         private ExceptionPlaceholder cause;
         private StackTraceElement[] stackTrace;
 
-        public ExceptionPlaceholder(Throwable throwable) throws IOException {
+        public ExceptionPlaceholder(final Throwable throwable) throws IOException {
             ByteArrayOutputStream outstr = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(outstr);
+            ObjectOutputStream oos = new ExceptionReplacingObjectOutputStream(outstr) {
+                @Override
+                protected Object replaceObject(Object obj) throws IOException {
+                    if (obj == throwable) {
+                        return throwable;
+                    }
+                    // Don't serialize the cause - we'll serialize it separately later 
+                    if (obj == throwable.getCause()) {
+                        return new CausePlaceholder();
+                    }
+                    return super.replaceObject(obj);
+                }
+            };
             try {
                 oos.writeObject(throwable);
                 oos.close();
@@ -65,19 +77,28 @@ public abstract class Message implements Serializable {
         }
 
         public Throwable read(ClassLoader classLoader) throws IOException {
+            final Throwable causeThrowable = getCause(classLoader);
+            Throwable throwable = null;
             if (serializedException != null) {
                 try {
-                    return (Throwable) new ClassLoaderObjectInputStream(new ByteArrayInputStream(serializedException),
-                            classLoader).readObject();
+                    final ExceptionReplacingObjectInputStream ois = new ExceptionReplacingObjectInputStream(new ByteArrayInputStream(serializedException), classLoader) {
+                        @Override
+                        protected Object resolveObject(Object obj) throws IOException {
+                            if (obj instanceof CausePlaceholder) {
+                                return causeThrowable;
+                            }
+                            return super.resolveObject(obj);
+                        }
+                    };
+                    throwable = (Throwable) ois.readObject();
                 } catch (ClassNotFoundException e) {
                     // Ignore
                 } catch (InvalidClassException e) {
                     try {
                         Constructor<?> constructor = classLoader.loadClass(type).getConstructor(String.class);
-                        Throwable throwable = (Throwable) constructor.newInstance(message);
-                        throwable.initCause(getCause(classLoader));
+                        throwable = (Throwable) constructor.newInstance(message);
+                        throwable.initCause(causeThrowable);
                         throwable.setStackTrace(stackTrace);
-                        return throwable;
                     } catch (ClassNotFoundException e1) {
                         // Ignore
                     } catch (NoSuchMethodException e1) {
@@ -88,10 +109,12 @@ public abstract class Message implements Serializable {
                 }
             }
 
-            PlaceholderException exception = new PlaceholderException(String.format("%s: %s", type, message), getCause(
-                    classLoader));
-            exception.setStackTrace(stackTrace);
-            return exception;
+            if (throwable == null) {
+                throwable = new PlaceholderException(String.format("%s: %s", type, message), causeThrowable);
+                throwable.setStackTrace(stackTrace);
+            }
+
+            return throwable;
         }
 
         private Throwable getCause(ClassLoader classLoader) throws IOException {
@@ -99,6 +122,9 @@ public abstract class Message implements Serializable {
         }
     }
 
+    private static class CausePlaceholder implements Serializable {
+    }
+
     private static class TopLevelExceptionPlaceholder extends ExceptionPlaceholder {
         private TopLevelExceptionPlaceholder(Throwable throwable) throws IOException {
             super(throwable);
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/BaseExecSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/BaseExecSpec.java
index b5c4d58..be8fdb5 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/BaseExecSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/BaseExecSpec.java
@@ -20,6 +20,8 @@ import java.io.OutputStream;
 import java.util.List;
 
 /**
+ * Specifies options for launching a child process.
+ *
  * @author Hans Dockter
  */
 public interface BaseExecSpec extends ProcessForkOptions {
@@ -40,7 +42,8 @@ public interface BaseExecSpec extends ProcessForkOptions {
     boolean isIgnoreExitValue();
 
     /**
-     * Sets the standard input stream for the process executing the command.
+     * Sets the standard input stream for the process executing the command. The stream is closed after the process
+     * completes.
      * 
      * @param inputStream The standard input stream for the command process.
      *
@@ -54,7 +57,8 @@ public interface BaseExecSpec extends ProcessForkOptions {
     InputStream getStandardInput();
 
     /**
-     * Sets the standard output stream for the process executing the command.
+     * Sets the standard output stream for the process executing the command. The stream is closed after the process
+     * completes.
      *
      * @param outputStream The standard output stream for the command process.
      *
@@ -63,7 +67,8 @@ public interface BaseExecSpec extends ProcessForkOptions {
     BaseExecSpec setStandardOutput(OutputStream outputStream);
 
     /**
-     * Sets the error output stream for the process executing the command.
+     * Sets the error output stream for the process executing the command. The stream is closed after the process
+     * completes.
      *
      * @param outputStream The standard output error stream for the command process.
      *
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/ExecSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/ExecSpec.java
index 7d0ada5..da845c9 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/ExecSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/ExecSpec.java
@@ -18,6 +18,8 @@ package org.gradle.process;
 import java.util.List;
 
 /**
+ * Specified the options for executing some command.
+ *
  * @author Hans Dockter
  */
 public interface ExecSpec extends BaseExecSpec {
@@ -65,7 +67,7 @@ public interface ExecSpec extends BaseExecSpec {
     ExecSpec setArgs(Iterable<?> args);
 
     /**
-     * Returns the args for the command to be executed
+     * Returns the args for the command to be executed.
      */
     List<String> getArgs();
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/JavaExecSpec.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/JavaExecSpec.java
index da302c6..8c5222c 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/JavaExecSpec.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/JavaExecSpec.java
@@ -20,6 +20,8 @@ import org.gradle.api.file.FileCollection;
 import java.util.List;
 
 /**
+ * Specifies the options for executing a Java application.
+ *
  * @author Hans Dockter
  */
 public interface JavaExecSpec extends JavaForkOptions, BaseExecSpec {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java
index 80bf940..7ad2f23 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/DefaultWorkerProcessFactory.java
@@ -17,6 +17,7 @@
 package org.gradle.process.internal;
 
 import org.gradle.api.internal.ClassPathRegistry;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.messaging.remote.MessagingServer;
@@ -36,7 +37,7 @@ import java.net.URL;
 import java.util.List;
 import java.util.concurrent.Callable;
 
-public class DefaultWorkerProcessFactory implements WorkerProcessFactory {
+public class DefaultWorkerProcessFactory implements Factory<WorkerProcessBuilder> {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultWorkerProcessFactory.class);
     private final LogLevel workerLogLevel;
     private final MessagingServer server;
@@ -54,7 +55,7 @@ public class DefaultWorkerProcessFactory implements WorkerProcessFactory {
         this.idGenerator = idGenerator;
     }
 
-    public WorkerProcessBuilder newProcess() {
+    public WorkerProcessBuilder create() {
         return new DefaultWorkerProcessBuilder();
     }
 
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java
index 2f22b3c..527ea50 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecHandleRunner.java
@@ -16,6 +16,10 @@
 
 package org.gradle.process.internal;
 
+import org.gradle.messaging.concurrent.DefaultExecutorFactory;
+import org.gradle.util.DisconnectableInputStream;
+
+import java.io.InputStream;
 import java.util.concurrent.Executor;
 
 /**
@@ -56,6 +60,7 @@ public class ExecHandleRunner implements Runnable {
             ExecOutputHandleRunner standardOutputRunner;
             ExecOutputHandleRunner errorOutputRunner;
             ExecOutputHandleRunner standardInputRunner;
+            InputStream instr = new DisconnectableInputStream(execHandle.getStandardInput(), new DefaultExecutorFactory());
             Process process;
 
             // This big fat static lock is here for windows. When starting multiple processes concurrently, the stdout
@@ -68,7 +73,7 @@ public class ExecHandleRunner implements Runnable {
                 errorOutputRunner = new ExecOutputHandleRunner("read process error output", process.getErrorStream(),
                         execHandle.getErrorOutput());
                 standardInputRunner = new ExecOutputHandleRunner("write process standard input",
-                        execHandle.getStandardInput(), process.getOutputStream());
+                        instr, process.getOutputStream());
             }
             synchronized (lock) {
                 this.process = process;
@@ -81,6 +86,7 @@ public class ExecHandleRunner implements Runnable {
             execHandle.started();
 
             exitCode = process.waitFor();
+            instr.close();
         } catch (Throwable t) {
             execHandle.failed(t);
             return;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecOutputHandleRunner.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecOutputHandleRunner.java
index 1159148..76efd76 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecOutputHandleRunner.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ExecOutputHandleRunner.java
@@ -48,6 +48,7 @@ public class ExecOutputHandleRunner implements Runnable {
                     break;
                 }
                 outputStream.write(buffer, 0, nread);
+                outputStream.flush();
             }
             new CompositeStoppable(inputStream, outputStream).stop();
         } catch (Throwable t) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java
index a753c6f..d23db03 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/ProcessBuilderFactory.java
@@ -34,14 +34,13 @@ public class ProcessBuilderFactory {
     public ProcessBuilder createProcessBuilder(ExecHandle execHandle) {
         final List<String> commandWithArguments = new ArrayList<String>();
         final String command = execHandle.getCommand();
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("creating process builder for {}", execHandle);
-        }
         commandWithArguments.add(command);
         final List<String> arguments = execHandle.getArguments();
         if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("creating process builder for {}", execHandle);
+            LOGGER.debug("in directory {}", execHandle.getDirectory());
             int argumentIndex = 0;
-            for ( String argument : arguments ) {
+            for (String argument : arguments) {
                 LOGGER.debug("with argument#{} = {}", argumentIndex, argument);
                 argumentIndex++;
             }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/WorkerProcessFactory.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/WorkerProcessFactory.java
deleted file mode 100644
index 5e70002..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/WorkerProcessFactory.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.process.internal;
-
-public interface WorkerProcessFactory {
-    WorkerProcessBuilder newProcess();
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java
index d095d7f..61b068a 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/internal/child/ImplementationClassLoaderWorker.java
@@ -18,7 +18,6 @@ package org.gradle.process.internal.child;
 
 import org.gradle.api.Action;
 import org.gradle.api.logging.LogLevel;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.LoggingServiceRegistry;
 import org.gradle.util.*;
@@ -81,7 +80,7 @@ public class ImplementationClassLoaderWorker implements Action<WorkerContext>, S
     }
 
     LoggingManagerInternal createLoggingManager() {
-        return new LoggingServiceRegistry().get(LoggingManagerFactory.class).create();
+        return new LoggingServiceRegistry().newInstance(LoggingManagerInternal.class);
     }
 
     ObservableUrlClassLoader createImplementationClassLoader(ClassLoader system, ClassLoader application) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/process/package-info.java b/subprojects/gradle-core/src/main/groovy/org/gradle/process/package-info.java
new file mode 100644
index 0000000..0e6ccea
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/process/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for executing system and Java processes.
+ */
+package org.gradle.process;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/profile/BuildProfile.java b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/BuildProfile.java
new file mode 100644
index 0000000..dc5601e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/BuildProfile.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.profile;
+
+import org.gradle.api.Project;
+import org.gradle.api.invocation.Gradle;
+
+import java.util.*;
+
+/**
+ * Root container for profile information about a build.  This includes summary
+ * information about the overall build timing and collection of project specific
+ * information.  All timing information is stored as milliseconds since epoch times.
+ * <p>
+ * Setters are expected to be called in the following order:
+ * <ul>
+ * <li>setProfilingStarted</li>
+ * <li>setBuildStarted</li>
+ * <li>setSettingsEvaluated</li>
+ * <li>setProjectsLoaded</li>
+ * <li>setProjectsEvaluated</li>
+ * <li>setBuildFinished</li>
+ * </ul>
+ */
+public class BuildProfile {
+    private Gradle gradle;
+    Map<Project, ProjectProfile> projects = new HashMap<Project, ProjectProfile>();
+    long profilingStarted;
+    long buildStarted;
+    long settingsEvaluated;
+    long projectsLoaded;
+    long projectsEvaluated;
+    long buildFinished;
+
+    public BuildProfile(Gradle gradle) {
+        this.gradle = gradle;
+    }
+
+    public Gradle getGradle() {
+        return gradle;
+    }
+
+    /**
+     * Get a description of the tasks passed to gradle as targets from the command line
+     * @return
+     */
+    public String getTaskDescription() {
+        StringBuilder result = new StringBuilder();
+        for (String name : gradle.getStartParameter().getExcludedTaskNames()) {
+            result.append("-x");
+            result.append(name);
+            result.append(" ");
+        }
+        for (String name : gradle.getStartParameter().getTaskNames()) {
+            result.append(name);
+            result.append(" ");
+        }
+        return result.toString();
+    }
+
+    /**
+     * Get the profiling container for the specified project
+     * @param project to look up
+     * @return
+     */
+    public ProjectProfile getProjectProfile(Project project) {
+        ProjectProfile result = projects.get(project);
+        if (result == null) {
+            result = new ProjectProfile(project);
+            projects.put(project, result);
+        }
+        return result;
+    }
+
+    /**
+     * Get a list of the profiling containers for all projects
+     * @return list
+     */
+    public List<ProjectProfile> getProjects() {
+        return new ArrayList<ProjectProfile>(projects.values());
+    }
+
+    /**
+     * Should be set with a time as soon as possible after startup.
+     * @param profilingStarted
+     */
+    public void setProfilingStarted(long profilingStarted) {
+        this.profilingStarted = profilingStarted;
+    }
+
+    /**
+     * Should be set with a timestamp from a {@link org.gradle.BuildListener#buildStarted}
+     * callback.
+     * @param buildStarted
+     */
+    public void setBuildStarted(long buildStarted) {
+        this.buildStarted = buildStarted;
+    }
+
+    /**
+     * Should be set with a timestamp from a {@link org.gradle.BuildListener#settingsEvaluated}
+     * callback.
+     * @param settingsEvaluated
+     */
+    public void setSettingsEvaluated(long settingsEvaluated) {
+        this.settingsEvaluated = settingsEvaluated;
+    }
+
+    /**
+     * Should be set with a timestamp from a {@link org.gradle.BuildListener#projectsLoaded}
+     * callback.
+     * @param projectsLoaded
+     */
+    public void setProjectsLoaded(long projectsLoaded) {
+        this.projectsLoaded = projectsLoaded;
+    }
+
+    /**
+     * Should be set with a timestamp from a {@link org.gradle.BuildListener#projectsEvaluated}
+     * callback.
+     * @param projectsEvaluated
+     */
+    public void setProjectsEvaluated(long projectsEvaluated) {
+        this.projectsEvaluated = projectsEvaluated;
+    }
+
+    /**
+     * Should be set with a timestamp from a {@link org.gradle.BuildListener#buildFinished}
+     * callback.
+     * @param buildFinished
+     */
+    public void setBuildFinished(long buildFinished) {
+        this.buildFinished = buildFinished;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) between the start of profiling and the buildStarted event.
+     * @return
+     */
+    public long getElapsedStartup() {
+        return buildStarted - profilingStarted;
+    }
+
+    /**
+     * Get the total elapsed time (in mSec) between the start of profiling and the buildFinished event.
+     * @return
+     */
+    public long getElapsedTotal() { 
+    return buildFinished - profilingStarted;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) between the buildStarted event and the settingsEvaluated event.
+     * Note that this will include processing of buildSrc as well as the settings file.
+     * @return
+     */
+    public long getElapsedSettings() {
+        return settingsEvaluated - buildStarted;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) between the settingsEvaluated event and the projectsLoaded event.
+     * @return
+     */
+    public long getElapsedProjectsLoading() {
+        return projectsLoaded - settingsEvaluated;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) between the projectsEvaluated event and the projectsLoaded event.
+     * @return
+     */
+    public long getElapsedProjectsEvaluated() {
+        return projectsEvaluated - projectsLoaded;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) between the buildFinished event and the projectsEvaluated event.
+     * @return
+     */
+    public long getElapsedAfterProjectsEvaluated() {
+        return buildFinished - projectsEvaluated;
+    }
+
+    /**
+     * Get the total task execution time from all projects.
+     * @return
+     */
+    public long getElapsedTotalExecutionTime() {
+        long result = 0;
+        for (ProjectProfile projectProfile : projects.values()) {
+            result += projectProfile.getElapsedTaskExecution();
+        }
+        return result;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ElapsedTimeFormatter.java b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ElapsedTimeFormatter.java
new file mode 100644
index 0000000..1a82898
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ElapsedTimeFormatter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.profile;
+
+import java.text.DecimalFormat;
+
+public class ElapsedTimeFormatter {
+    private static final DecimalFormat SECONDS_FORMAT = new DecimalFormat("0.000");
+    private static final DecimalFormat SECONDS_FORMAT2 = new DecimalFormat("00.000");
+    private static final DecimalFormat HMFORMAT = new DecimalFormat("0");
+    private static final DecimalFormat HMFORMAT2 = new DecimalFormat("00");
+
+    public String format(long elapsed) {
+        long hours = elapsed / (1000L * 60 * 60);
+        long minutes = (elapsed / (1000L * 60)) % 60;
+        float seconds = (float) ((elapsed % 60000L) / 1000.0);
+        StringBuffer result = new StringBuffer();
+
+        if (hours > 0) {
+            result.append(HMFORMAT.format(hours));
+            result.append(":");
+        }
+
+        if (minutes > 0) {
+            if (hours > 0) {
+                result.append(HMFORMAT2.format(minutes));
+            } else {
+                result.append(HMFORMAT.format(minutes));
+            }
+            result.append(":");
+        }
+
+        if (hours > 0 || minutes > 0) {
+            result.append(SECONDS_FORMAT2.format(seconds));
+        } else {
+            result.append(SECONDS_FORMAT.format(seconds));
+        }
+
+        return result.toString();
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/profile/HTMLProfileReport.groovy b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/HTMLProfileReport.groovy
new file mode 100644
index 0000000..c9c59c2
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/HTMLProfileReport.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.profile
+
+import java.text.SimpleDateFormat
+import org.gradle.api.UncheckedIOException
+import groovy.text.SimpleTemplateEngine
+
+
+class HTMLProfileReport {
+    private BuildProfile profile;
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd - HH:mm:ss");
+    private static final ElapsedTimeFormatter TIME_FORMAT = new ElapsedTimeFormatter();
+
+    public HTMLProfileReport(BuildProfile profile) {
+        this.profile = profile
+    }
+
+    public void writeTo(PrintWriter out) {
+        try {
+            InputStream templateStream = getClass().getResourceAsStream('/org/gradle/profile/ProfileTemplate.html')
+            SimpleTemplateEngine engine = new SimpleTemplateEngine()
+            def binding = ['build': profile, 'time': TIME_FORMAT, 'date':DATE_FORMAT]
+            def result = engine.createTemplate(new InputStreamReader(templateStream)).make(binding)
+            result.writeTo(out)
+        }
+        finally {
+            out.close();
+        }
+    }
+
+    public void writeTo(File file) {
+        try {
+            FileOutputStream out = new FileOutputStream(file);
+            writeTo(new PrintWriter(new BufferedOutputStream(out)));
+        } catch (FileNotFoundException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ProfileListener.java b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ProfileListener.java
new file mode 100644
index 0000000..6fb78ce
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ProfileListener.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.profile;
+
+import org.gradle.BuildListener;
+import org.gradle.BuildResult;
+import org.gradle.api.*;
+import org.gradle.api.execution.TaskExecutionListener;
+import org.gradle.api.initialization.Settings;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.api.tasks.TaskState;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ProfileListener implements BuildListener, ProjectEvaluationListener, TaskExecutionListener {
+    private BuildProfile buildProfile;
+    private Map<Project, ProjectProfile> projects = new HashMap<Project, ProjectProfile>();
+    private static final SimpleDateFormat FILE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+    private long profileStarted;
+
+    public ProfileListener(long profileStarted) {
+        this.profileStarted = profileStarted;
+    }
+
+    // BuildListener
+    public void buildStarted(Gradle gradle) {
+        buildProfile = new BuildProfile(gradle);
+        buildProfile.setBuildStarted(System.currentTimeMillis());
+        buildProfile.setProfilingStarted(profileStarted);
+    }
+
+    public void settingsEvaluated(Settings settings) {
+        buildProfile.setSettingsEvaluated(System.currentTimeMillis());
+    }
+
+    public void projectsLoaded(Gradle gradle) {
+        buildProfile.setProjectsLoaded(System.currentTimeMillis());
+    }
+
+    public void projectsEvaluated(Gradle gradle) {
+        buildProfile.setProjectsEvaluated(System.currentTimeMillis());
+    }
+
+    public void buildFinished(BuildResult result) {
+        buildProfile.setBuildFinished(System.currentTimeMillis());
+
+        HTMLProfileReport report = new HTMLProfileReport(buildProfile);
+        File file = new File(result.getGradle().getRootProject().getBuildDir(), "reports/profile/profile-"+
+                FILE_DATE_FORMAT.format(new Date(profileStarted)) + ".html" );
+        file.getParentFile().mkdirs();
+        try {
+            file.createNewFile();
+            report.writeTo(file);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    // ProjectEvaluationListener
+    public void beforeEvaluate(Project project) {
+        buildProfile.getProjectProfile(project).setBeforeEvaluate(System.currentTimeMillis());
+    }
+
+    public void afterEvaluate(Project project, ProjectState state) {
+        ProjectProfile projectProfile = buildProfile.getProjectProfile(project);
+        projectProfile.setAfterEvaluate(System.currentTimeMillis());
+        projectProfile.setState(state);
+    }
+
+
+    // TaskExecutionListener
+    public void beforeExecute(Task task) {
+        Project project = task.getProject();
+        ProjectProfile projectProfile = buildProfile.getProjectProfile(project);
+        projectProfile.getTaskProfile(task).setStart(System.currentTimeMillis());
+    }
+
+    public void afterExecute(Task task, TaskState state) {
+        Project project = task.getProject();
+        ProjectProfile projectProfile = buildProfile.getProjectProfile(project);
+        TaskProfile taskProfile = projectProfile.getTaskProfile(task);
+        taskProfile.setFinish(System.currentTimeMillis());
+        taskProfile.setState(state);
+    }
+}
+
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ProjectProfile.java b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ProjectProfile.java
new file mode 100644
index 0000000..1144144
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/ProjectProfile.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.profile;
+
+import org.gradle.api.Project;
+import org.gradle.api.ProjectState;
+import org.gradle.api.Task;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ProjectProfile {
+    private Project project;
+    private long beforeEvaluate;
+    private long afterEvaluate;
+    private ProjectState state;
+    private HashMap<Task, TaskProfile> tasks = new HashMap<Task, TaskProfile>();
+
+    public ProjectProfile(Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Gets the task profiling container for the specified task.
+     * @param task
+     * @return
+     */
+    public TaskProfile getTaskProfile(Task task) {
+        TaskProfile result = tasks.get(task);
+        if (result == null) {
+            result = new TaskProfile(task);
+            tasks.put(task, result);
+        }
+        return result;
+    }
+
+    /**
+     * Gets the list of task profiling containers.
+     * @return
+     */
+    public List<TaskProfile> getTaskProfiles() {
+        return new ArrayList<TaskProfile>(tasks.values());
+    }
+
+    /**
+     * Get the String project path.
+     * @return
+     */
+    public String getPath() {
+        return project.getPath();
+    }
+
+    /**
+     * Should be set with a timestamp right before project evaluation begins.
+     * @param beforeEvaluate
+     */
+    public void setBeforeEvaluate(long beforeEvaluate) {
+        this.beforeEvaluate = beforeEvaluate;
+    }
+
+    /**
+     * Should be set with a timestamp right after proejct evaluation finishes.
+     * @param afterEvaluate
+     */
+    public void setAfterEvaluate(long afterEvaluate) {
+        this.afterEvaluate = afterEvaluate;
+    }
+
+    /**
+     * Gets the state of the project after evaluation finishes.
+     * @return
+     */
+    public ProjectState getState() {
+        return state;
+    }
+
+    public void setState(ProjectState state) {
+        this.state = state;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) for project evaluation (configuration phase).
+     * @return
+     */
+    public long getElapsedEvaluation() {
+        return afterEvaluate - beforeEvaluate;
+    }
+
+    /**
+     * Get the elapsed time (in mSec) for execution of all tasks.
+     * @return
+     */
+    public long getElapsedTaskExecution() {
+        long result = 0;
+        for (TaskProfile taskProfile : tasks.values()) {
+            result += taskProfile.getElapsedExecution();
+        }
+        return result;
+    }
+}
+
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/profile/TaskProfile.java b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/TaskProfile.java
new file mode 100644
index 0000000..2a5055a
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/profile/TaskProfile.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.profile;
+
+import org.gradle.api.Task;
+import org.gradle.api.tasks.TaskState;
+
+/**
+ * Container for task profiling information.
+ * This includes timestamps around task execution and the resulting TaskState.
+ */
+public class TaskProfile {
+    private Task task;
+    private long start;
+    private long finish;
+    private TaskState state;
+
+    public TaskProfile(Task task) {
+        this.task = task;
+    }
+
+    /**
+     * Gets the string task path.
+     * @return
+     */
+    public String getPath() {
+        return task.getPath();
+    }
+
+    /**
+     * Should be called with a time right before task execution begins.
+     * @param start
+     */
+    public void setStart(long start) {
+        this.start = start;
+    }
+
+    /**
+     * Should be called with a time right after task execution finishes.
+     * @param finish
+     */
+    public void setFinish(long finish) {
+        this.finish = finish;
+    }
+
+    /**
+     * Gets the elapsed task execution time (in mSec).
+     * @return
+     */
+    public long getElapsedExecution() {
+        return finish - start;
+    }
+
+    public TaskState getState() {
+        return state;
+    }
+
+    public void setState(TaskState state) {
+        this.state = state;
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java b/subprojects/gradle-core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java
index 951618d..879c0cf 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/testfixtures/ProjectBuilder.java
@@ -18,10 +18,7 @@ package org.gradle.testfixtures;
 import org.gradle.StartParameter;
 import org.gradle.api.Project;
 import org.gradle.api.UncheckedIOException;
-import org.gradle.api.internal.ClassPathRegistry;
-import org.gradle.api.internal.DefaultClassPathRegistry;
-import org.gradle.api.internal.GradleDistributionLocator;
-import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.*;
 import org.gradle.api.internal.project.*;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.LoggingManager;
@@ -36,11 +33,15 @@ import org.gradle.initialization.DefaultProjectDescriptorRegistry;
 import org.gradle.invocation.DefaultGradle;
 import org.gradle.listener.DefaultListenerManager;
 import org.gradle.listener.ListenerManager;
-import org.gradle.logging.DefaultProgressLoggerFactory;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.logging.internal.DefaultProgressLoggerFactory;
+import org.gradle.logging.internal.DefaultStyledTextOutputFactory;
+import org.gradle.logging.internal.OutputEventListener;
+import org.gradle.logging.internal.ProgressListener;
 import org.gradle.util.GFileUtils;
+import org.gradle.util.TrueTimeProvider;
 
 import java.io.File;
 import java.io.IOException;
@@ -150,7 +151,6 @@ public class ProjectBuilder {
             return this;
         }
 
-        @Override
         public LogLevel getStandardErrorCaptureLevel() {
             return LogLevel.ERROR;
         }
@@ -174,6 +174,15 @@ public class ProjectBuilder {
 
         public void removeStandardErrorListener(StandardOutputListener listener) {
         }
+
+        public void addOutputEventListener(OutputEventListener listener) {
+        }
+
+        public void removeOutputEventListener(OutputEventListener listener) {
+        }
+
+        public void colorStdOutAndStdErr(boolean colorOutput) {
+        }
     }
 
     private static class GlobalTestServices extends DefaultServiceRegistry {
@@ -194,17 +203,21 @@ public class ProjectBuilder {
         }
 
         protected ProgressLoggerFactory createProgressLoggerFactory() {
-            return new DefaultProgressLoggerFactory(get(ListenerManager.class));
+            return new DefaultProgressLoggerFactory(get(ListenerManager.class).getBroadcaster(ProgressListener.class), new TrueTimeProvider());
         }
 
-        protected LoggingManagerFactory createLoggingManagerFactory() {
-            return new LoggingManagerFactory() {
+        protected Factory<LoggingManagerInternal> createLoggingManagerFactory() {
+            return new Factory<LoggingManagerInternal>() {
                 public LoggingManagerInternal create() {
                     return new NoOpLoggingManager();
                 }
             };
         }
 
+        protected StyledTextOutputFactory createStyledTextOutputFactory() {
+            return new DefaultStyledTextOutputFactory(get(ListenerManager.class).getBroadcaster(OutputEventListener.class), new TrueTimeProvider());
+        }
+        
         protected IsolatedAntBuilder createIsolatedAntBuilder() {
             return new DefaultIsolatedAntBuilder(get(ClassPathRegistry.class));
         }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/BulkReadInputStream.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/BulkReadInputStream.java
new file mode 100644
index 0000000..0ed9ad6
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/BulkReadInputStream.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class BulkReadInputStream extends InputStream {
+    @Override
+    public int read() throws IOException {
+        byte[] buffer = new byte[1];
+        while (true) {
+            int nread = read(buffer);
+            if (nread < 0) {
+                return -1;
+            }
+            if (nread == 1) {
+                return 0xff & buffer[0];
+            }
+        }
+    }
+
+    @Override
+    public abstract int read(byte[] bytes, int pos, int count) throws IOException;
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/DisconnectableInputStream.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/DisconnectableInputStream.java
new file mode 100644
index 0000000..7b0770e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/DisconnectableInputStream.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.util;
+
+import org.gradle.messaging.concurrent.ExecutorFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * An {@code InputStream} which reads from the source {@code InputStream}. In addition, when the {@code InputStream} is
+ * closed, all threads blocked reading from the stream will receive an end-of-stream.
+ */
+public class DisconnectableInputStream extends BulkReadInputStream {
+    private final Lock lock = new ReentrantLock();
+    private final Condition condition = lock.newCondition();
+    private final byte[] buffer;
+    private int readPos;
+    private int writePos;
+    private boolean closed;
+    private boolean inputFinished;
+
+    public DisconnectableInputStream(final InputStream source, ExecutorFactory executorFactory) {
+        this(source, executorFactory, 1024);
+    }
+
+    public DisconnectableInputStream(final InputStream source, ExecutorFactory executorFactory, int bufferLength) {
+        buffer = new byte[bufferLength];
+        executorFactory.create("read input").execute(new Runnable() {
+            public void run() {
+                try {
+                    while (true) {
+                        int pos;
+                        lock.lock();
+                        try {
+                            while (!closed && writePos == buffer.length && writePos != readPos) {
+                                // buffer is full, wait until it has been read
+                                condition.await();
+                            }
+                            assert writePos >= readPos;
+                            if (closed) {
+                                // stream has been closed, don't bother reading anything else
+                                inputFinished = true;
+                                condition.signalAll();
+                                return;
+                            }
+                            if (readPos == writePos) {
+                                // buffer has been fully read, start at the beginning
+                                readPos = 0;
+                                writePos = 0;
+                            }
+                            pos = writePos;
+                        } finally {
+                            lock.unlock();
+                        }
+
+                        int nread = source.read(buffer, pos, buffer.length - pos);
+
+                        lock.lock();
+                        try {
+                            if (nread > 0) {
+                                // Have read some data - let readers know
+                                assert writePos >= readPos;
+                                writePos += nread;
+                                assert buffer.length >= writePos;
+                                condition.signalAll();
+                            }
+                            if (closed || nread < 0) {
+                                // End of the stream
+                                inputFinished = true;
+                                condition.signalAll();
+                                return;
+                            }
+                        } finally {
+                            lock.unlock();
+                        }
+                    }
+                } catch (Throwable throwable) {
+                    lock.lock();
+                    try {
+                        inputFinished = true;
+                        condition.signalAll();
+                    } finally {
+                        lock.unlock();
+                    }
+                    throw UncheckedException.asUncheckedException(throwable);
+                }
+            }
+        });
+    }
+
+    @Override
+    public int read(byte[] bytes, int pos, int count) throws IOException {
+        lock.lock();
+        int nread;
+        try {
+            while (!inputFinished && !closed && readPos == writePos) {
+                condition.await();
+            }
+            if (inputFinished && readPos == writePos) {
+                return -1;
+            }
+            if (closed) {
+                return -1;
+            }
+            assert writePos > readPos;
+            nread = Math.min(count, writePos - readPos);
+            System.arraycopy(buffer, readPos, bytes, pos, nread);
+            readPos += nread;
+            assert writePos >= readPos;
+            condition.signalAll();
+        } catch (InterruptedException e) {
+            throw UncheckedException.asUncheckedException(e);
+        } finally {
+            lock.unlock();
+        }
+        return nread;
+    }
+
+    /**
+     * Closes this {@code InputStream} for reading. Any threads blocked in read() will receive a {@link
+     * java.nio.channels.AsynchronousCloseException}. Also requests that the reader thread stop reading, if possible,
+     * but does not block waiting for this to happen.
+     *
+     * <p>NOTE: this method does not close the source input stream.</p>
+     */
+    @Override
+    public void close() throws IOException {
+        lock.lock();
+        try {
+            closed = true;
+            condition.signalAll();
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/GUtil.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/GUtil.java
index a38fbc1..6f993cf 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/GUtil.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/GUtil.java
@@ -16,14 +16,14 @@
 
 package org.gradle.util;
 
-import org.gradle.api.UncheckedIOException;
 import org.apache.commons.lang.StringUtils;
+import org.gradle.api.UncheckedIOException;
 
 import java.io.*;
 import java.net.URL;
 import java.util.*;
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * @author Hans Dockter
@@ -132,33 +132,16 @@ public class GUtil {
 
     public static Comparator<String> caseInsensitive() {
         return new Comparator<String>() {
-            @Override
-            public int compare(String o1, String o2) {
-                return o1.compareToIgnoreCase(o2);
-            }
-        };
-    }
-
-    public static Comparator<String> emptyLast(final Comparator<String> comparator) {
-        return new Comparator<String>() {
-            @Override
             public int compare(String o1, String o2) {
-                boolean o1Empty = o1 == null || o1.isEmpty();
-                boolean o2Empty = o2 == null || o2.isEmpty();
-                if (o1Empty && o2Empty) {
-                    return 0;
-                }
-                if (o1Empty && !o2Empty) {
-                    return 1;
-                }
-                if (!o1Empty && o2Empty) {
-                    return -1;
+                int diff = o1.compareToIgnoreCase(o2);
+                if (diff != 0) {
+                    return diff;
                 }
-                return comparator.compare(o1, o2);
+                return o1.compareTo(o2);
             }
         };
     }
-    
+
     public static Map addMaps(Map map1, Map map2) {
         HashMap map = new HashMap();
         map.putAll(map1);
@@ -176,8 +159,13 @@ public class GUtil {
 
     public static Properties loadProperties(File propertyFile) {
         try {
-            return loadProperties(new FileInputStream(propertyFile));
-        } catch (FileNotFoundException e) {
+            FileInputStream inputStream = new FileInputStream(propertyFile);
+            try {
+                return loadProperties(inputStream);
+            } finally {
+                inputStream.close();
+            }
+        } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
     }
@@ -204,8 +192,11 @@ public class GUtil {
     public static void saveProperties(Properties properties, File propertyFile) {
         try {
             FileOutputStream propertiesFileOutputStream = new FileOutputStream(propertyFile);
-            properties.store(propertiesFileOutputStream, null);
-            propertiesFileOutputStream.close();
+            try {
+                properties.store(propertiesFileOutputStream, null);
+            } finally {
+                propertiesFileOutputStream.close();
+            }
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
@@ -297,4 +288,23 @@ public class GUtil {
         }
         return outputStream.toByteArray();
     }
+
+    public static <T> Comparator<T> last(final Comparator<? super T> comparator, final T lastValue) {
+        return new Comparator<T>() {
+            public int compare(T o1, T o2) {
+                boolean o1Last = comparator.compare(o1, lastValue) == 0;
+                boolean o2Last = comparator.compare(o2, lastValue) == 0;
+                if (o1Last && o2Last) {
+                    return 0;
+                }
+                if (o1Last && !o2Last) {
+                    return 1;
+                }
+                if (!o1Last && o2Last) {
+                    return -1;
+                }
+                return comparator.compare(o1, o2);
+            }
+        };
+    }
 }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/GradleVersion.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/GradleVersion.java
index 664ecda..5ab0787 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/GradleVersion.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/GradleVersion.java
@@ -16,11 +16,11 @@
 
 package org.gradle.util ;
 
-import org.codehaus.groovy.runtime.InvokerHelper ;
-import org.apache.ivy.Ivy ;
-import org.apache.tools.ant.Main ;
+import groovy.lang.GroovySystem;
+import org.apache.ivy.Ivy;
+import org.apache.tools.ant.Main;
 
-import java.util.Properties ;
+import java.util.Properties;
 
 /**
  * @author Hans Dockter
@@ -50,22 +50,18 @@ public class GradleVersion {
     final StringBuilder sb = new StringBuilder ( ) ;
     sb.append ( "\n------------------------------------------------------------\nGradle " ) ;
     sb.append ( getVersion ( ) ) ;
-    sb.append ( "\n------------------------------------------------------------\n\nGradle buildtime: " ) ;
+    sb.append ( "\n------------------------------------------------------------\n\nGradle build time: " ) ;
     sb.append ( getBuildTime ( ) ) ;
     sb.append ( "\nGroovy: " ) ;
-    sb.append ( InvokerHelper.getVersion ( ) ) ;
+    sb.append ( GroovySystem.getVersion ( ) ) ;
     sb.append ( "\nAnt: " ) ;
     sb.append ( Main.getAntVersion ( ) ) ;
     sb.append ( "\nIvy: " ) ;
     sb.append ( Ivy.getIvyVersion ( ) ) ;
-    sb.append ( "\nJava: " ) ;
-    sb.append ( System.getProperty ( "java.version" ) ) ;
     sb.append ( "\nJVM: " ) ;
-    sb.append ( System.getProperty ( "java.vm.version" ) ) ;
-    sb.append ( "\nJVM Vendor: " ) ;
-    sb.append ( System.getProperty ( "java.vm.vendor" ) ) ;
-    sb.append ( "\nOS Name: " ) ;
-    sb.append ( System.getProperty ( "os.name" ) ) ;
+    sb.append ( Jvm.current() ) ;
+    sb.append ( "\nOS: " ) ;
+    sb.append ( OperatingSystem.current() ) ;
     sb.append ( "\n" ) ;
     return sb.toString ( ) ;
   }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/HashUtil.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/HashUtil.java
index 2548385..b3feba9 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/HashUtil.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/HashUtil.java
@@ -34,7 +34,7 @@ public class HashUtil {
         try {
             messageDigest = MessageDigest.getInstance("MD5");
         } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException(e);
+            throw UncheckedException.asUncheckedException(e);
         }
         messageDigest.update(scriptText.getBytes());
         return new BigInteger(1, messageDigest.digest()).toString(16);
@@ -45,7 +45,7 @@ public class HashUtil {
         try {
             messageDigest = MessageDigest.getInstance("MD5");
         } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException(e);
+            throw UncheckedException.asUncheckedException(e);
         }
         try {
             byte[] buffer = new byte[4096];
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/IgnoreInterruptHandler.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/IgnoreInterruptHandler.java
deleted file mode 100644
index 8200978..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/IgnoreInterruptHandler.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.util;
-
-/**
- * @author Tom Eyckmans
- */
-public class IgnoreInterruptHandler<T> implements InterruptHandler<T> {
-    public boolean handleIterrupt(T interruptedThead, InterruptedException interruptedException) {
-        return false;
-    }
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/InterruptHandler.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/InterruptHandler.java
deleted file mode 100644
index 84c6aaa..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/InterruptHandler.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.util;
-
-/**
- * @author Tom Eyckmans
- */
-public interface InterruptHandler<T> {
-    boolean handleIterrupt(T interrupted, InterruptedException interruptedException);
-}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/JavaMethod.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/JavaMethod.java
index d6934ce..fd4b9dc 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/JavaMethod.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/JavaMethod.java
@@ -32,6 +32,12 @@ public class JavaMethod<T, R> {
         method.setAccessible(true);
     }
 
+    public JavaMethod(Class<T> target, Class<R> returnType, Method method) {
+        this.returnType = returnType;
+        this.method = method;
+        method.setAccessible(true);
+    }
+
     private Method findMethod(Class target, String name, Class<?>[] paramTypes) {
         for (Method method : target.getDeclaredMethods()) {
             if (Modifier.isStatic(method.getModifiers())) {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/Jvm.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/Jvm.java
index 54b0bd8..5f695e3 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/Jvm.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/Jvm.java
@@ -28,6 +28,11 @@ public class Jvm {
     Jvm() {
     }
 
+    @Override
+    public String toString() {
+        return String.format("%s (%s %s)", System.getProperty("java.version"), System.getProperty("java.vm.vendor"), System.getProperty("java.vm.version"));
+    }
+
     public File getJavaExecutable() {
         return new File(JavaEnvUtils.getJdkExecutable("java"));
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
index f9075d1..5bf8a16 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/LineBufferingOutputStream.java
@@ -111,7 +111,7 @@ public class LineBufferingOutputStream extends OutputStream {
      * by the implementation of the output stream, such bytes should immediately be written to their intended
      * destination.
      */
-    public void flush() throws IOException {
+    public void flush() {
         if (count != 0) {
             int length = count;
             if (endsWithLineSeparator()) {
@@ -130,7 +130,7 @@ public class LineBufferingOutputStream extends OutputStream {
     }
 
     private interface Output {
-        void write(byte[] buffer, int textLength, int lineLength) throws IOException;
+        void write(byte[] buffer, int textLength, int lineLength);
     }
 
     private static class StringOutput implements Output {
@@ -142,7 +142,7 @@ public class LineBufferingOutputStream extends OutputStream {
             this.action = action;
         }
 
-        public void write(byte[] buffer, int textLength, int lineLength) throws IOException {
+        public void write(byte[] buffer, int textLength, int lineLength) {
             if (includeEOL) {
                 action.execute(new String(buffer, 0, lineLength));
             } else {
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/LinePerThreadBufferingOutputStream.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/LinePerThreadBufferingOutputStream.java
index 1ae192a..1928b21 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/LinePerThreadBufferingOutputStream.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/LinePerThreadBufferingOutputStream.java
@@ -54,6 +54,18 @@ public class LinePerThreadBufferingOutputStream extends PrintStream {
     }
 
     @Override
+    public PrintStream append(CharSequence csq) {
+        getStream().append(csq);
+        return this;
+    }
+
+    @Override
+    public PrintStream append(char c) {
+        getStream().append(c);
+        return this;
+    }
+
+    @Override
     public PrintStream append(CharSequence csq, int start, int end) {
         getStream().append(csq, start, end);
         return this;
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/OperatingSystem.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/OperatingSystem.java
index 26ccec6..5adfb59 100644
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/OperatingSystem.java
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/OperatingSystem.java
@@ -61,6 +61,11 @@ public class OperatingSystem {
         return CURRENT;
     }
 
+    @Override
+    public String toString() {
+        return String.format("%s %s %s", System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("os.arch"));
+    }
+
     public boolean isWindows() {
         return false;
     }
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/Path.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/Path.java
new file mode 100644
index 0000000..f433fbf
--- /dev/null
+++ b/subprojects/gradle-core/src/main/groovy/org/gradle/util/Path.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.util;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Project;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * @author Hans Dockter
+ */
+public class Path implements Comparable<Path> {
+    private static final Comparator<String> STRING_COMPARATOR = GUtil.caseInsensitive();
+    private final String prefix;
+    private final String[] segments;
+    private final boolean absolute;
+    public static final Path ROOT = new Path(Project.PATH_SEPARATOR);
+
+    public Path(String path) {
+        assert path.length() > 0;
+        segments = StringUtils.split(path, Project.PATH_SEPARATOR);
+        absolute = path.startsWith(Project.PATH_SEPARATOR);
+        prefix = path.endsWith(Project.PATH_SEPARATOR) ? path : path + Project.PATH_SEPARATOR;
+    }
+
+    private Path(String[] segments, boolean absolute) {
+        this.segments = segments;
+        this.absolute = absolute;
+        this.prefix = getPath() + Project.PATH_SEPARATOR;
+    }
+
+    public static Path path(String path) {
+        return path.equals(Project.PATH_SEPARATOR) ? ROOT : new Path(path);
+    }
+
+    @Override
+    public String toString() {
+        return getPath();
+    }
+
+    public String getPath() {
+        StringBuilder path = new StringBuilder();
+        if (absolute) {
+            path.append(Project.PATH_SEPARATOR);
+        }
+        for (int i = 0; i < segments.length; i++) {
+            if (i > 0) {
+                path.append(Project.PATH_SEPARATOR);
+            }
+            String segment = segments[i];
+            path.append(segment);
+        }
+        return path.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || obj.getClass() != getClass()) {
+            return false;
+        }
+
+        Path other = (Path) obj;
+        return Arrays.equals(segments, other.segments);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(segments);
+    }
+
+    public int compareTo(Path other) {
+        if (absolute && !other.absolute) {
+            return 1;
+        }
+        if (!absolute && other.absolute) {
+            return -1;
+        }
+        for (int i = 0; i < Math.min(segments.length, other.segments.length); i++) {
+            int diff = STRING_COMPARATOR.compare(segments[i], other.segments[i]);
+            if (diff != 0) {
+                return diff;
+            }
+        }
+        int lenDiff = segments.length - other.segments.length;
+        if (lenDiff > 0) {
+            return 1;
+        }
+        if (lenDiff < 0) {
+            return -1;
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the parent of this path, or null if this path has no parent.
+     *
+     * @return The parent of this path.
+     */
+    public Path getParent() {
+        if (segments.length == 0) {
+            return null;
+        }
+        if (segments.length == 1) {
+            return absolute ? ROOT : null;
+        }
+        String[] parentPath = new String[segments.length - 1];
+        System.arraycopy(segments, 0, parentPath, 0, parentPath.length);
+        return new Path(parentPath, absolute);
+    }
+
+    /**
+     * Returns the base name of this path, or null if this path is the root path.
+     *
+     * @return The base name,
+     */
+    public String getName() {
+        if (segments.length == 0) {
+            return null;
+        }
+        return segments[segments.length - 1];
+    }
+
+    public Path resolve(String path) {
+        return new Path(absolutePath(path));
+    }
+
+    public String absolutePath(String path) {
+        if (!isAbsolutePath(path)) {
+            return prefix + path;
+        }
+        return path;
+    }
+
+    public String relativePath(String path) {
+        if (path.startsWith(prefix) && path.length() > prefix.length()) {
+            return path.substring(prefix.length());
+        }
+        return path;
+    }
+
+    private boolean isAbsolutePath(String path) {
+        if (!GUtil.isTrue(path)) {
+            throw new InvalidUserDataException("A path must be specified!");
+        }
+        return path.startsWith(Project.PATH_SEPARATOR);
+    }
+}
diff --git a/subprojects/gradle-core/src/main/groovy/org/gradle/util/PathHelper.java b/subprojects/gradle-core/src/main/groovy/org/gradle/util/PathHelper.java
deleted file mode 100644
index 8fa6cd3..0000000
--- a/subprojects/gradle-core/src/main/groovy/org/gradle/util/PathHelper.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-package org.gradle.util;
-
-import org.gradle.api.Project;
-import org.gradle.api.InvalidUserDataException;
-
-/**
- * @author Hans Dockter
- */
-public class PathHelper {
-    public static boolean isAbsolutePath(String path) {
-        if (!GUtil.isTrue(path)) {
-            throw new InvalidUserDataException("A path must be specified!");
-        }
-        return path.startsWith(Project.PATH_SEPARATOR);
-    }
-}
diff --git a/subprojects/gradle-core/src/main/resources/org/gradle/profile/ProfileTemplate.html b/subprojects/gradle-core/src/main/resources/org/gradle/profile/ProfileTemplate.html
new file mode 100644
index 0000000..a37550e
--- /dev/null
+++ b/subprojects/gradle-core/src/main/resources/org/gradle/profile/ProfileTemplate.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+    <title>Profile</title>
+    <style type="text/css">
+        .date { text-align:right; }
+        td { vertical-align:top;}
+        .et { text-align:right; }
+        table table { border: solid black 1px;}
+        table table tr:nth-child(even) { background-color: #ddf; }
+        td {
+            vertical-align:top;
+            padding-left:5px;
+            padding-right:5px;
+            white-space:nowrap;
+        }
+        h1 {
+            font-size:100%;
+            font-family:sans-serif;
+        }
+        .indentPath { padding-left: 3em; }
+        .heading {
+            text-align:center;
+            font-family:sans-serif;
+            font-weight:bold;
+        }
+    </style>
+
+</head>
+<body>
+    <div class="header">
+        <span></span>
+        <span class="date"></span>
+    </div>
+    <table>
+        <tr>
+            <td>Profiled with tasks: ${build.taskDescription}</td>
+            <td> </td>
+            <td class="date">Run on: ${date.format(build.buildStarted)}</td>
+        </tr>
+        <tr>
+            <td>
+                <div id="summary">
+                    <div class="heading">Summary</div>
+                    <table cellpadding="0" cellspacing="0">
+                        <tr>
+                            <td>Total Build Time</td>
+                            <td class="et">${time.format(build.elapsedTotal)}</td>
+                        </tr>
+                        <tr>
+                            <td>Startup</td>
+                            <td class="et">${time.format(build.elapsedStartup)}</td>
+                        </tr>
+                        <tr>
+                            <td>Settings and BuildSrc</td>
+                            <td class="et">${time.format(build.elapsedSettings)}</td>
+                        </tr>
+                        <tr>
+                            <td>Loading Projects</td>
+                            <td class="et">${time.format(build.elapsedProjectsLoading)}</td>
+                        </tr>
+                        <tr>
+                            <td>Configuring Projects</td>
+                            <td class="et">${time.format(build.elapsedProjectsEvaluated)}</td>
+                        </tr>
+                        <tr>
+                            <td>Total Task Execution</td>
+                            <td class="et">${time.format(build.elapsedTotalExecutionTime)}</td>
+                        </tr>
+                    </table>
+                </div>
+            </td>
+            <td>
+                <div id="config">
+                    <div class="heading">Configuration</div>
+                    <table cellpadding="0" cellspacing="0">
+                        <%
+                            def projects = build.projects
+                            projects.sort { it. elapsedEvaluation }
+                            projects = projects.reverse()
+
+                            for (def project : projects) {
+                        %>
+                        <tr>
+                            <td>$project.path</td>
+                            <td class="et">${time.format(project.elapsedEvaluation)}</td>
+                        </tr>
+                        <% } %>
+                    </table>
+                </div>
+            </td>
+            <td>
+                <div id="execution">
+                    <div class="heading">Task Execution</div>
+                    <table cellpadding="0" cellspacing="0">
+                        <%
+                            projects.sort { it.elapsedTaskExecution }
+                            projects = projects.reverse()
+                            for (def project : projects) {
+                        %>
+                        <tr>
+                            <td>$project.path</td>
+                            <td class="et">${time.format(project.elapsedTaskExecution)}</td>
+                            <td>(total)</td>
+                        </tr>
+                        <%
+                            def profiles = project.taskProfiles
+                            profiles.sort { it.elapsedExecution }
+                            profiles = profiles.reverse()
+                            for (def task : profiles) {
+                        %>
+                        <tr>
+                            <td class="indentPath">${task.path}</td>
+                            <td class="et">${time.format(task.elapsedExecution)}</td>
+                            <td><%= task.state.getSkipped() ? task.state.skipMessage : (task.state.didWork ? ' ' : 'Did No Work')%></td>
+                        </tr>
+                        <%
+                            }
+                        %>
+                        <% } %>
+                    </table>
+                </div>
+
+            </td>
+        </tr>
+    </table>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
new file mode 100644
index 0000000..9f00c10
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/BuildExceptionReporterTest.groovy
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle
+
+import spock.lang.Specification
+import org.gradle.logging.internal.TestStyledTextOutput
+import org.gradle.logging.StyledTextOutputFactory
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.GradleException
+import org.gradle.api.LocationAwareException
+import org.gradle.StartParameter.ShowStacktrace
+import org.gradle.execution.TaskSelectionException
+
+class BuildExceptionReporterTest extends Specification {
+    final TestStyledTextOutput output = new TestStyledTextOutput()
+    final StyledTextOutputFactory factory = Mock()
+    final StartParameter startParameter = new StartParameter()
+    final BuildExceptionReporter reporter = new BuildExceptionReporter(factory, startParameter)
+
+    def setup() {
+        _ * factory.create(BuildExceptionReporter.class, LogLevel.ERROR) >> output
+    }
+
+    def doesNothingWheBuildIsSuccessful() {
+        expect:
+        reporter.buildFinished(result(null))
+        output.value == ''
+    }
+
+    def reportsInternalFailure() {
+        final RuntimeException exception = new RuntimeException("<message>");
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build aborted because of an internal error.{normal}
+
+* What went wrong:
+Build aborted because of an unexpected internal error. Please file an issue at: http://www.gradle.org.
+
+* Try:
+Run with {userinput}-d{normal} option to get additional debug info.
+
+* Exception is:
+java.lang.RuntimeException: <message>
+{stacktrace}
+'''
+    }
+
+    def reportsBuildFailure() {
+        GradleException exception = new GradleException("<message>");
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* What went wrong:
+<message>
+
+* Try:
+Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more details. Run with {userinput}-S{normal} option to get the full (very verbose) stacktrace.
+'''
+    }
+
+    def reportsBuildFailureWhenFailureHasNoMessage() {
+        GradleException exception = new GradleException();
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* What went wrong:
+org.gradle.api.GradleException (no error message)
+
+* Try:
+Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more details. Run with {userinput}-S{normal} option to get the full (very verbose) stacktrace.
+'''
+    }
+
+    def reportsLocationAwareException() {
+        Throwable exception = exception("<location>", "<message>", new RuntimeException("<cause>"));
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* Where:
+<location>
+
+* What went wrong:
+<message>
+Cause: <cause>
+
+* Try:
+Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more details. Run with {userinput}-S{normal} option to get the full (very verbose) stacktrace.
+'''
+    }
+
+    def reportsLocationAwareExceptionWithMultipleCauses() {
+        Throwable exception = exception("<location>", "<message>", new RuntimeException("<outer>"), new RuntimeException("<cause>"));
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* Where:
+<location>
+
+* What went wrong:
+<message>
+Cause: <outer>
+Cause: <cause>
+
+* Try:
+Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more details. Run with {userinput}-S{normal} option to get the full (very verbose) stacktrace.
+'''
+    }
+
+    def reportsLocationAwareExceptionWhenCauseHasNoMessage() {
+        Throwable exception = exception("<location>", "<message>", new RuntimeException());
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* Where:
+<location>
+
+* What went wrong:
+<message>
+Cause: java.lang.RuntimeException (no error message)
+
+* Try:
+Run with {userinput}-s{normal} or {userinput}-d{normal} option to get more details. Run with {userinput}-S{normal} option to get the full (very verbose) stacktrace.
+'''
+    }
+
+    def reportsTaskSelectionException() {
+        Throwable exception = new TaskSelectionException("<message>");
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Could not determine which tasks to execute.{normal}
+
+* What went wrong:
+<message>
+
+* Try:
+Run {userinput}gradle tasks{normal} to get a list of available tasks.
+'''
+    }
+
+    def reportsBuildFailureWhenShowStacktraceEnabled() {
+        startParameter.showStacktrace = ShowStacktrace.ALWAYS
+
+        GradleException exception = new GradleException('<message>')
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* What went wrong:
+<message>
+
+* Try:
+Run with {userinput}-d{normal} option to get more details. Run with {userinput}-S{normal} option to get the full (very verbose) stacktrace.
+
+* Exception is:
+org.gradle.api.GradleException: <message>
+{stacktrace}
+'''
+    }
+
+    def reportsBuildFailureWhenShowFullStacktraceEnabled() {
+        startParameter.showStacktrace = ShowStacktrace.ALWAYS_FULL
+
+        GradleException exception = new GradleException('<message>')
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* What went wrong:
+<message>
+
+* Try:
+Run with {userinput}-d{normal} option to get more details. 
+
+* Exception is:
+org.gradle.api.GradleException: <message>
+{stacktrace}
+'''
+    }
+
+    def reportsBuildFailureWhenDebugLoggingEnabled() {
+        startParameter.logLevel = LogLevel.DEBUG
+
+        GradleException exception = new GradleException('<message>')
+
+        expect:
+        reporter.buildFinished(result(exception))
+        output.value == '''
+{failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
+
+* What went wrong:
+<message>
+
+* Exception is:
+org.gradle.api.GradleException: <message>
+{stacktrace}
+'''
+    }
+
+    def result(Throwable failure) {
+        BuildResult result = Mock()
+        _ * result.failure >> failure
+        result
+    }
+
+    def exception(final String location, final String message, final Throwable... causes) {
+        TestException exception = Mock()
+        _ * exception.location >> location
+        _ * exception.originalMessage >> message
+        _ * exception.reportableCauses >> (causes as List)
+        exception
+    }
+}
+
+public abstract class TestException extends GradleException implements LocationAwareException {
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/BuildExceptionReporterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/BuildExceptionReporterTest.java
deleted file mode 100644
index e492cd9..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/BuildExceptionReporterTest.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.LocationAwareException;
-import org.gradle.util.HelperUtil;
-import org.hamcrest.Matcher;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.slf4j.Logger;
-
-import java.util.Arrays;
-
-import static org.hamcrest.Matchers.*;
-
- at RunWith(JMock.class)
-public class BuildExceptionReporterTest {
-    private final JUnit4Mockery context = new JUnit4Mockery(){{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
-    private Logger logger = context.mock(Logger.class);
-    private StartParameter startParameter = new StartParameter();
-    private BuildExceptionReporter reporter = new BuildExceptionReporter(logger, startParameter);
-
-    @Test
-    public void reportsBuildFailure() {
-        final GradleException exception = new GradleException("<message>");
-        final Matcher<String> errorMessage = allOf(containsString("Build failed with an exception."),
-                containsString("<message>"));
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(errorMessage));
-        }});
-
-        reporter.buildFinished(HelperUtil.createBuildResult(exception));
-    }
-
-    @Test
-    public void reportsBuildFailureWhenCauseHasNoMessage() {
-        final GradleException exception = new GradleException();
-        final Matcher<String> errorMessage = allOf(containsString("Build failed with an exception."),
-                containsString(GradleException.class.getName() + " (no error message)"));
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(errorMessage));
-        }});
-
-        reporter.buildFinished(HelperUtil.createBuildResult(exception));
-    }
-
-    @Test
-    public void reportsLocationAwareException() {
-        final TestException exception = exception("<location>", "<message>", new RuntimeException("<cause>"));
-
-        final Matcher<String> errorMessage = allOf(containsString("Build failed with an exception."),
-                containsString("<location>"),
-                containsString("<message>"),
-                containsString("Cause: <cause>"));
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(errorMessage));
-        }});
-
-        reporter.buildFinished(HelperUtil.createBuildResult(exception));
-    }
-
-    private TestException exception(final String location, final String message, final Throwable... causes) {
-        final TestException testException = context.mock(TestException.class);
-        context.checking(new Expectations() {{
-            allowing(testException).getLocation();
-            will(returnValue(location));
-            allowing(testException).getOriginalMessage();
-            will(returnValue(message));
-            allowing(testException).getReportableCauses();
-            will(returnValue(Arrays.asList(causes)));
-        }});
-        return testException;
-    }
-
-    @Test
-    public void reportsLocationAwareExceptionWithMultipleCauses() {
-        final TestException exception = exception("<location>", "<message>", new RuntimeException("<outer>"),
-                new RuntimeException("<cause>"));
-
-        final Matcher<String> errorMessage = allOf(containsString("Build failed with an exception."),
-                containsString("<location>"),
-                containsString("<message>"),
-                containsString("Cause: <outer>"),
-                containsString("<cause>"));
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(errorMessage));
-        }});
-
-        reporter.buildFinished(HelperUtil.createBuildResult(exception));
-    }
-
-    @Test
-    public void reportsLocationAwareExceptionWhenCauseHasNoMessage() {
-        final TestException exception = exception("<location>", "<message>", new RuntimeException());
-
-        final Matcher<String> errorMessage = allOf(containsString("Build failed with an exception."),
-                containsString("<location>"),
-                containsString("<message>"),
-                containsString("Cause: " + RuntimeException.class.getName() + " (no error message)"));
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(errorMessage));
-        }});
-
-        reporter.buildFinished(HelperUtil.createBuildResult(exception));
-    }
-
-    @Test
-    public void reportsBuildFailureWhenOptionsHaveNotBeenSet() {
-        final GradleException exception = new GradleException("<message>");
-        final Matcher<String> errorMessage = allOf(containsString("Build failed with an exception."),
-                containsString("<message>"));
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(errorMessage));
-        }});
-
-        reporter = new BuildExceptionReporter(logger, startParameter);
-        reporter.buildFinished(HelperUtil.createBuildResult(exception));
-    }
-
-    @Test
-    public void reportsInternalFailure() {
-        final RuntimeException failure = new RuntimeException("<message>");
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(containsString("Build aborted because of an internal error.")), with(sameInstance(failure)));
-        }});
-
-        reporter.buildFinished(HelperUtil.createBuildResult(failure));
-    }
-
-    @Test
-    public void reportsInternalFailureWhenOptionsHaveNotBeenSet() {
-        final RuntimeException failure = new RuntimeException("<message>");
-
-        context.checking(new Expectations() {{
-            one(logger).error(with(containsString("Build aborted because of an internal error.")), with(sameInstance(failure)));
-        }});
-
-        reporter = new BuildExceptionReporter(logger, startParameter);
-        reporter.buildFinished(HelperUtil.createBuildResult(failure));
-    }
-
-    @Test
-    public void doesNothingWheBuildIsSuccessful() {
-        reporter.buildFinished(HelperUtil.createBuildResult(null));
-    }
-
-    public abstract class TestException extends GradleException implements LocationAwareException {
-    }
-
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/BuildResultLoggerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/BuildResultLoggerTest.java
index bdac39b..1a218d7 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/BuildResultLoggerTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/BuildResultLoggerTest.java
@@ -15,54 +15,60 @@
  */
 package org.gradle;
 
-import static org.hamcrest.Matchers.*;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.logging.StyledTextOutputFactory;
+import org.gradle.logging.internal.TestStyledTextOutput;
+import org.gradle.util.Clock;
+import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
-import org.jmock.lib.legacy.ClassImposteriser;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.gradle.api.logging.Logger;
-import org.gradle.util.Clock;
+
+import static org.junit.Assert.assertEquals;
 
 @RunWith(org.jmock.integration.junit4.JMock.class)
 public class BuildResultLoggerTest {
-    private final JUnit4Mockery context = new JUnit4Mockery(){{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
-    private Logger logger;
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private StyledTextOutputFactory textOutputFactory;
+    private TestStyledTextOutput textOutput;
     private BuildListener listener;
     private Clock buildTimeClock;
 
     @Before
     public void setup() {
-        logger = context.mock(Logger.class);
+        textOutput = new TestStyledTextOutput();
         buildTimeClock = context.mock(Clock.class);
-        listener = new BuildResultLogger(logger, buildTimeClock);
+        textOutputFactory = context.mock(StyledTextOutputFactory.class);
+        listener = new BuildResultLogger(textOutputFactory, buildTimeClock);
     }
 
     @Test
     public void logsBuildSuccessAndTotalTime() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(buildTimeClock).getTime();
             will(returnValue("10s"));
-            one(logger).lifecycle(String.format("%nBUILD SUCCESSFUL%n"));
-            one(logger).lifecycle(with(startsWith("Total time: 10s")));
+            one(textOutputFactory).create(BuildResultLogger.class, LogLevel.LIFECYCLE);
+            will(returnValue(textOutput));
         }});
 
         listener.buildFinished(new BuildResult(null, null));
+
+        assertEquals("\n{success}BUILD SUCCESSFUL{normal}\n\nTotal time: 10s\n", textOutput.getValue());
     }
 
     @Test
     public void logsBuildFailedAndTotalTime() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(buildTimeClock).getTime();
             will(returnValue("10s"));
-            one(logger).error(String.format("%nBUILD FAILED%n"));
-            one(logger).lifecycle(with(startsWith("Total time: 10s")));
+            one(textOutputFactory).create(BuildResultLogger.class, LogLevel.LIFECYCLE);
+            will(returnValue(textOutput));
         }});
 
         listener.buildFinished(new BuildResult(null, new RuntimeException()));
-    }
 
+        assertEquals("\n{failure}BUILD FAILED{normal}\n\nTotal time: 10s\n", textOutput.getValue());
+    }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/StartParameterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/StartParameterTest.groovy
index 3a4f72e..ef2226b 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/StartParameterTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/StartParameterTest.groovy
@@ -18,7 +18,7 @@ package org.gradle
 
 import static org.gradle.util.Matchers.*
 
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction
 import org.gradle.api.logging.LogLevel
 import org.gradle.execution.BuildExecuter
 import org.gradle.execution.DefaultBuildExecuter
@@ -56,6 +56,8 @@ class StartParameterTest {
         testObj.gradleUserHomeDir = new File('b')
         testObj.initScripts = [new File('init script'), new File("/path/to/another init script")]
         testObj.cacheUsage = CacheUsage.ON
+        testObj.logLevel = LogLevel.WARN
+        testObj.colorOutput = false
 
         StartParameter startParameter = testObj.newInstance()
         assertEquals(testObj, startParameter)
@@ -70,6 +72,7 @@ class StartParameterTest {
         assertThat(parameter.settingsScriptSource, nullValue())
 
         assertThat(parameter.logLevel, equalTo(LogLevel.LIFECYCLE))
+        assertTrue(parameter.colorOutput)
         assertThat(parameter.taskNames, isEmpty())
         assertThat(parameter.excludedTaskNames, isEmpty())
         assertThat(parameter.projectProperties, isEmptyMap())
@@ -222,6 +225,7 @@ class StartParameterTest {
         parameter.gradleUserHomeDir = new File("home")
         parameter.cacheUsage = CacheUsage.REBUILD
         parameter.logLevel = LogLevel.DEBUG
+        parameter.colorOutput = false
 
         // Non-copied
         parameter.currentDir = new File("other")
@@ -239,6 +243,7 @@ class StartParameterTest {
         assertThat(newParameter.gradleUserHomeDir, equalTo(parameter.gradleUserHomeDir));
         assertThat(newParameter.cacheUsage, equalTo(parameter.cacheUsage));
         assertThat(newParameter.logLevel, equalTo(parameter.logLevel));
+        assertThat(newParameter.colorOutput, equalTo(parameter.colorOutput));
 
         assertThat(newParameter.buildFile, nullValue())
         assertThat(newParameter.taskNames, isEmpty())
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java
index c1cf114..110eb72 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/TaskExecutionLoggerTest.java
@@ -57,7 +57,7 @@ public class TaskExecutionLoggerTest {
         context.checking(new Expectations() {{
             allowing(gradle).getParent();
             will(returnValue(null));
-            one(progressLoggerFactory).start(":path");
+            one(progressLoggerFactory).start(TaskExecutionLogger.class.getName(), ":path");
             will(returnValue(progressLogger));
             one(progressLogger).progress(":path");
         }});
@@ -85,7 +85,7 @@ public class TaskExecutionLoggerTest {
             allowing(rootProject).getName();
             will(returnValue("build"));
 
-            one(progressLoggerFactory).start(":build:path");
+            one(progressLoggerFactory).start(TaskExecutionLogger.class.getName(), ":build:path");
             will(returnValue(progressLogger));
             one(progressLogger).progress(":build:path");
         }});
@@ -106,7 +106,7 @@ public class TaskExecutionLoggerTest {
         context.checking(new Expectations() {{
             allowing(gradle).getParent();
             will(returnValue(null));
-            one(progressLoggerFactory).start(":path");
+            one(progressLoggerFactory).start(TaskExecutionLogger.class.getName(), ":path");
             will(returnValue(progressLogger));
             one(progressLogger).progress(":path");
         }});
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/ProjectDependenciesBuildInstructionTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/ProjectDependenciesBuildInstructionTest.java
deleted file mode 100644
index a35cecb..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/ProjectDependenciesBuildInstructionTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts;
-
-import org.junit.Test;
-import static org.junit.Assert.assertThat;
-import static org.hamcrest.Matchers.*;
-import org.gradle.util.WrapUtil;
-
-import java.util.Collections;
-
-/**
- * @author Hans Dockter
- */
-public class ProjectDependenciesBuildInstructionTest {
-    @Test
-    public void initWithNull() {
-        ProjectDependenciesBuildInstruction buildInstruction = new ProjectDependenciesBuildInstruction(null);
-        assertThat(buildInstruction.isRebuild(), equalTo(false));
-        assertThat(buildInstruction.getTaskNames(), equalTo(Collections.<String>emptyList()));
-    }
-
-    @Test
-    public void initWithEmptyList() {
-        ProjectDependenciesBuildInstruction buildInstruction = new ProjectDependenciesBuildInstruction(Collections.<String>emptyList());
-        assertThat(buildInstruction.isRebuild(), equalTo(true));
-        assertThat(buildInstruction.getTaskNames(), equalTo(Collections.<String>emptyList()));
-    }
-
-    @Test
-    public void initWithNonEmptyList() {
-        String taskName = "someTaskName";
-        ProjectDependenciesBuildInstruction buildInstruction = new ProjectDependenciesBuildInstruction(
-                WrapUtil.toList(taskName));
-        assertThat(buildInstruction.isRebuild(), equalTo(true));
-        assertThat(buildInstruction.getTaskNames(), equalTo(WrapUtil.toList(taskName)));
-    }
-
-    @Test
-    public void equality() {
-        String taskName = "someTaskName";
-        assertThat(new ProjectDependenciesBuildInstruction(null),
-                equalTo(new ProjectDependenciesBuildInstruction(null)));
-        assertThat(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()),
-                equalTo(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList())));
-        assertThat(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)),
-                equalTo(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName))));
-
-        assertThat(new ProjectDependenciesBuildInstruction(null),
-                not(equalTo(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()))));
-        assertThat(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)),
-                not(equalTo(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName + 'x')))));
-    }
-
-    @Test
-    public void hashCodeEquality() {
-        String taskName = "someTaskName";
-        assertThat(new ProjectDependenciesBuildInstruction(null).hashCode(),
-                equalTo(new ProjectDependenciesBuildInstruction(null).hashCode()));
-        assertThat(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()).hashCode(),
-                equalTo(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()).hashCode()));
-        assertThat(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)).hashCode(),
-                equalTo(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)).hashCode()));
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/specs/DependencySpecsTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/specs/DependencySpecsTest.java
new file mode 100644
index 0000000..5f72467
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/specs/DependencySpecsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.artifacts.specs;
+
+import org.gradle.api.artifacts.ExternalModuleDependency;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+
+import static org.gradle.util.Matchers.strictlyEqual;
+import static org.junit.Assert.*;
+
+/**
+ * @author Hans Dockter
+ */
+public class DependencySpecsTest {
+    private JUnit4Mockery context = new JUnit4Mockery();
+
+    @Test
+    public void testIsSatisfiedBy() {
+        assertTrue(DependencySpecs.type(Type.PROJECT).isSatisfiedBy(context.mock(ProjectDependency.class)));
+        assertFalse(DependencySpecs.type(Type.PROJECT).isSatisfiedBy(context.mock(ExternalModuleDependency.class)));
+    }
+
+    @Test
+    public void equality() {
+        assertThat(DependencySpecs.type(Type.PROJECT), strictlyEqual(DependencySpecs.type(Type.PROJECT)));
+        assertFalse(DependencySpecs.type(Type.PROJECT).equals(DependencySpecs.type(Type.EXTERNAL)));
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/specs/DependencyTypeSpecTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/specs/DependencyTypeSpecTest.java
deleted file mode 100644
index 6be53d8..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/artifacts/specs/DependencyTypeSpecTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.artifacts.specs;
-
-import org.gradle.api.artifacts.ExternalModuleDependency;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.*;
-import org.junit.Test;
-
-/**
- * @author Hans Dockter
- */
-public class DependencyTypeSpecTest {
-    private JUnit4Mockery context = new JUnit4Mockery();
-
-    @Test
-    public void init() {
-        assertSame(Type.PROJECT, new DependencyTypeSpec(Type.PROJECT).getType());
-    }
-
-    @Test
-    public void testIsSatisfiedBy() {
-        assertTrue(new DependencyTypeSpec(Type.PROJECT).isSatisfiedBy(context.mock(ProjectDependency.class)));
-        assertFalse(new DependencyTypeSpec(Type.PROJECT).isSatisfiedBy(context.mock(ExternalModuleDependency.class)));
-    }
-
-    @Test
-    public void equality() {
-        assertTrue(new DependencyTypeSpec(Type.PROJECT).equals(new DependencyTypeSpec(Type.PROJECT)));
-        assertFalse(new DependencyTypeSpec(Type.PROJECT).equals(new DependencyTypeSpec(Type.EXTERNAL)));
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy
new file mode 100644
index 0000000..ae43a0a
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/XmlTransformerTest.groovy
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal
+
+import spock.lang.Specification
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.Action
+
+class XmlTransformerTest extends Specification {
+    final XmlTransformer transformer = new XmlTransformer()
+
+    def returnsOriginalStringWhenNoActions() {
+        expect:
+        transformer.transform('<xml/>') == '<xml/>'
+    }
+
+    def actionCanAccessXmlAsStringBuffer() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+
+        when:
+        def result = transformer.transform('<xml/>')
+
+        then:
+        result == '<some-xml/>'
+        1 * action.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asString().insert(1, 'some-')
+        }
+    }
+
+    def actionCanAccessXmlAsNode() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+
+        when:
+        def result = transformer.transform('<xml/>')
+
+        then:
+        result == '<xml>\n  <child1/>\n</xml>\n'
+        1 * action.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asNode().appendNode('child1')
+        }
+    }
+
+    def actionCanAccessXmlAsDomElement() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+
+        when:
+        def result = transformer.transform('<xml/>')
+
+        then:
+        result == toNative('<?xml version="1.0" encoding="UTF-8"?>\n<xml>\n  <child1/>\n</xml>\n')
+        1 * action.execute(!null) >> { args ->
+            def provider = args[0]
+            def document = provider.asElement().ownerDocument
+            provider.asElement().appendChild(document.createElement('child1'))
+        }
+    }
+
+    def canTransformStringToAWriter() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        StringWriter writer = new StringWriter()
+
+        when:
+        transformer.transform('<xml/>', writer)
+
+        then:
+        writer.toString() == '<xml>\n  <child1/>\n</xml>\n'
+        1 * action.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asNode().appendNode('child1')
+        }
+    }
+
+    def canTransformNodeToAWriter() {
+        Action<XmlProvider> action = Mock()
+        transformer.addAction(action)
+        StringWriter writer = new StringWriter()
+        Node node = new XmlParser().parseText('<xml/>')
+
+        when:
+        transformer.transform(node, writer)
+
+        then:
+        writer.toString() == '<xml>\n  <child1/>\n</xml>\n'
+        1 * action.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asNode().appendNode('child1')
+        }
+    }
+
+    def canUseAClosureAsAnAction() {
+        transformer.addAction { provider ->
+            provider.asNode().appendNode('child1')
+        }
+        StringWriter writer = new StringWriter()
+
+        when:
+        transformer.transform('<xml/>', writer)
+
+        then:
+        writer.toString() == '<xml>\n  <child1/>\n</xml>\n'
+    }
+
+    def canChainActions() {
+        Action<XmlProvider> stringAction = Mock()
+        Action<XmlProvider> nodeAction = Mock()
+        Action<XmlProvider> elementAction = Mock()
+        Action<XmlProvider> stringAction2 = Mock()
+        transformer.addAction(stringAction)
+        transformer.addAction(elementAction)
+        transformer.addAction(nodeAction)
+        transformer.addAction(stringAction2)
+
+        when:
+        def result = transformer.transform('<xml/>')
+
+        then:
+        result == '<some-xml>\n  <child1/>\n  <child2/>\n</some-xml>\n<!-- end -->'
+        1 * stringAction.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asString().insert(1, 'some-')
+        }
+        1 * elementAction.execute(!null) >> { args ->
+            def provider = args[0]
+            def document = provider.asElement().ownerDocument
+            provider.asElement().appendChild(document.createElement('child1'))
+        }
+        1 * nodeAction.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asNode().appendNode('child2')
+        }
+        1 * stringAction2.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asString().append('<!-- end -->')
+        }
+    }
+
+    def canChainNodeActions() {
+        Action<XmlProvider> nodeAction = Mock()
+        Action<XmlProvider> nodeAction2 = Mock()
+        transformer.addAction(nodeAction)
+        transformer.addAction(nodeAction2)
+
+        when:
+        def result = transformer.transform('<xml/>')
+
+        then:
+        result == '<xml>\n  <child1/>\n  <child2/>\n</xml>\n'
+        1 * nodeAction.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asNode().appendNode('child1')
+        }
+        1 * nodeAction2.execute(!null) >> { args ->
+            def provider = args[0]
+            provider.asNode().appendNode('child2')
+        }
+    }
+
+    def String toNative(String value) {
+        return value.replaceAll('\n', System.getProperty('line.separator'))
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectDependenciesBuildInstructionTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectDependenciesBuildInstructionTest.java
new file mode 100644
index 0000000..8b57840
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ProjectDependenciesBuildInstructionTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.artifacts;
+
+import org.gradle.util.WrapUtil;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Hans Dockter
+ */
+public class ProjectDependenciesBuildInstructionTest {
+    @Test
+    public void initWithNull() {
+        ProjectDependenciesBuildInstruction buildInstruction = new ProjectDependenciesBuildInstruction(null);
+        assertThat(buildInstruction.isRebuild(), equalTo(false));
+        assertThat(buildInstruction.getTaskNames(), equalTo(Collections.<String>emptyList()));
+    }
+
+    @Test
+    public void initWithEmptyList() {
+        ProjectDependenciesBuildInstruction buildInstruction = new ProjectDependenciesBuildInstruction(Collections.<String>emptyList());
+        assertThat(buildInstruction.isRebuild(), equalTo(true));
+        assertThat(buildInstruction.getTaskNames(), equalTo(Collections.<String>emptyList()));
+    }
+
+    @Test
+    public void initWithNonEmptyList() {
+        String taskName = "someTaskName";
+        ProjectDependenciesBuildInstruction buildInstruction = new ProjectDependenciesBuildInstruction(
+                WrapUtil.toList(taskName));
+        assertThat(buildInstruction.isRebuild(), equalTo(true));
+        assertThat(buildInstruction.getTaskNames(), equalTo(WrapUtil.toList(taskName)));
+    }
+
+    @Test
+    public void equality() {
+        String taskName = "someTaskName";
+        assertThat(new ProjectDependenciesBuildInstruction(null),
+                equalTo(new ProjectDependenciesBuildInstruction(null)));
+        assertThat(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()),
+                equalTo(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList())));
+        assertThat(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)),
+                equalTo(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName))));
+
+        assertThat(new ProjectDependenciesBuildInstruction(null),
+                not(equalTo(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()))));
+        assertThat(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)),
+                not(equalTo(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName + 'x')))));
+    }
+
+    @Test
+    public void hashCodeEquality() {
+        String taskName = "someTaskName";
+        assertThat(new ProjectDependenciesBuildInstruction(null).hashCode(),
+                equalTo(new ProjectDependenciesBuildInstruction(null).hashCode()));
+        assertThat(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()).hashCode(),
+                equalTo(new ProjectDependenciesBuildInstruction(Collections.<String>emptyList()).hashCode()));
+        assertThat(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)).hashCode(),
+                equalTo(new ProjectDependenciesBuildInstruction(WrapUtil.toList(taskName)).hashCode()));
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.java
index c5495d7..4afe19c 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.java
@@ -141,7 +141,7 @@ public class DefaultConfigurationContainerTest {
 
     private void expectConfigurationCreated(final String name) {
         context.checking(new Expectations(){{
-            one(domainObjectContext).absolutePath(name);
+            one(domainObjectContext).absoluteProjectPath(name);
             will(returnValue(name));
             one(classGenerator).newInstance(DefaultConfiguration.class, name, name, configurationContainer, ivyServiceDummy);
             will(returnValue(new DefaultConfiguration(name, name, configurationContainer, ivyServiceDummy)));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java
index a6588b9..d182de5 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependencyTest.java
@@ -20,6 +20,7 @@ import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.*;
 import org.gradle.api.internal.artifacts.DependencyResolveContext;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.tasks.TaskContainer;
 import org.gradle.api.tasks.TaskDependency;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactoryTest.java
index bf6b41b..a923434 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultRepositoryHandlerFactoryTest.java
@@ -15,21 +15,19 @@
  */
 package org.gradle.api.internal.artifacts.dsl;
 
-import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
-import org.gradle.api.internal.plugins.DefaultConvention;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.internal.ClassGenerator;
-import org.gradle.api.internal.IConventionAware;
-import org.gradle.api.internal.ConventionMapping;
-import org.gradle.api.plugins.Convention;
-import static org.hamcrest.Matchers.*;
+import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory;
+import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.Expectations;
 import org.jmock.lib.legacy.ClassImposteriser;
-import static org.junit.Assert.*;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
+
 /**
  * @author Hans Dockter
  */
@@ -40,31 +38,20 @@ public class DefaultRepositoryHandlerFactoryTest {
     }};
     private ResolverFactory repositoryFactoryMock = context.mock(ResolverFactory.class);
     private ClassGenerator classGeneratorMock = context.mock(ClassGenerator.class);
-    private Convention convention = new DefaultConvention();
 
     @Test
-    public void createsARepositoryHandlerAndSetsConvention() {
-        final ConventionAwareRepositoryHandler repositoryHandlerMock = context.mock(ConventionAwareRepositoryHandler.class);
-        final ConventionMapping conventionMappingMock = context.mock(ConventionMapping.class);
+    public void createsARepositoryHandler() {
+        final RepositoryHandler repositoryHandlerMock = context.mock(DefaultRepositoryHandler.class);
 
         context.checking(new Expectations() {{
             one(classGeneratorMock).newInstance(DefaultRepositoryHandler.class, repositoryFactoryMock, classGeneratorMock);
             will(returnValue(repositoryHandlerMock));
-            allowing(repositoryHandlerMock).getConventionMapping();
-            will(returnValue(conventionMappingMock));
-            one(conventionMappingMock).setConvention(convention);
         }});
 
         DefaultRepositoryHandlerFactory repositoryHandlerFactory = new DefaultRepositoryHandlerFactory(
                 repositoryFactoryMock, classGeneratorMock);
-        DefaultRepositoryHandler repositoryHandler = repositoryHandlerFactory.createRepositoryHandler(convention);
+        DefaultRepositoryHandler repositoryHandler = repositoryHandlerFactory.create();
         assertThat(repositoryHandler, sameInstance((Object) repositoryHandlerMock));
     }
 
-    public static abstract class ConventionAwareRepositoryHandler extends DefaultRepositoryHandler
-            implements IConventionAware {
-        protected ConventionAwareRepositoryHandler(ResolverFactory resolverFactory, ClassGenerator classGenerator) {
-            super(resolverFactory, classGenerator);
-        }
-    }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactoryTest.java
index 54e21f3..6794e82 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/DefaultProjectDependencyFactoryTest.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.artifacts.dsl.dependencies;
 import org.gradle.api.IllegalDependencyNotation;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Dependency;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
 import org.gradle.api.internal.AsmBackedClassGenerator;
 import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency;
 import org.gradle.api.internal.project.ProjectInternal;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServicePublishTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServicePublishTest.java
index bd59fba..978f309 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServicePublishTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServicePublishTest.java
@@ -22,7 +22,7 @@ import org.apache.ivy.core.settings.IvySettings;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.repositories.InternalRepository;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.artifacts.configurations.Configurations;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.configurations.ResolverProvider;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServiceResolveTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServiceResolveTest.java
index f009500..24cc838 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServiceResolveTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultIvyServiceResolveTest.java
@@ -22,7 +22,7 @@ import org.apache.ivy.plugins.resolver.DependencyResolver;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Module;
 import org.gradle.api.artifacts.ResolvedConfiguration;
-import org.gradle.api.artifacts.repositories.InternalRepository;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.configurations.ResolverProvider;
 import org.gradle.util.HelperUtil;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java
index 728f882..4b16c80 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.java
@@ -19,7 +19,7 @@ import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.gradle.api.artifacts.ExternalModuleDependency;
 import org.gradle.api.artifacts.Module;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
 import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency;
 import org.gradle.api.internal.artifacts.ivyservice.IvyUtil;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
index 3374a84..d0188b5 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DefaultMavenPomFactoryTest.groovy
@@ -33,7 +33,7 @@ public class DefaultMavenPomFactoryTest extends Specification {
         FileResolver fileResolver = Mock(FileResolver); 
         DefaultMavenPomFactory mavenPomFactory = new DefaultMavenPomFactory(configurationContainer, scopeMappings,
                 pomDependenciesConverter, fileResolver);
-        DefaultMavenPom mavenPom = (DefaultMavenPom) mavenPomFactory.createMavenPom();
+        DefaultMavenPom mavenPom = (DefaultMavenPom) mavenPomFactory.create();
 
         expect:
         !scopeMappings.is(mavenPom.scopeMappings)
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java
index ff032bb..424e4af 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/DeployTaskFactoryTest.java
@@ -26,6 +26,6 @@ import org.junit.Test;
 public class DeployTaskFactoryTest {
     @Test
     public void create() {
-        assertTrue(new DefaultDeployTaskFactory().createDeployTask() instanceof CustomDeployTask);
+        assertTrue(new DefaultDeployTaskFactory().create() instanceof CustomDeployTask);
     }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
index 025bc86..17538b8 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenDeployerTest.java
@@ -23,6 +23,7 @@ import org.codehaus.plexus.PlexusContainerException;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.maven.MavenResolver;
 import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.Factory;
 import org.gradle.util.WrapUtil;
 import org.jmock.Expectations;
 import org.junit.Test;
@@ -42,7 +43,7 @@ public class BaseMavenDeployerTest extends AbstractMavenResolverTest {
 
     private BaseMavenDeployer mavenDeployer = createMavenDeployer();
 
-    private DeployTaskFactory deployTaskFactoryMock = context.mock(DeployTaskFactory.class);
+    private Factory<CustomDeployTask> deployTaskFactoryMock = context.mock(Factory.class);
     private CustomDeployTask deployTaskMock = context.mock(CustomDeployTask.class);
 
     private PlexusContainer plexusContainerMock = context.mock(PlexusContainer.class);
@@ -82,7 +83,7 @@ public class BaseMavenDeployerTest extends AbstractMavenResolverTest {
         context.checking(new Expectations() {{
                 allowing(configurationStub).resolve();
                 will(returnValue(protocolJars));
-                allowing(deployTaskFactoryMock).createDeployTask();
+                allowing(deployTaskFactoryMock).create();
                 will(returnValue(getInstallDeployTask()));
                 allowing(deployTaskMock).getContainer();
                 will(returnValue(plexusContainerMock));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
index 38f8dbc..c952097 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BaseMavenInstallerTest.java
@@ -20,6 +20,7 @@ import org.apache.maven.artifact.ant.InstallDeployTaskSupport;
 import org.codehaus.plexus.PlexusContainerException;
 import org.gradle.api.artifacts.maven.MavenResolver;
 import org.gradle.api.artifacts.maven.PomFilterContainer;
+import org.gradle.api.internal.Factory;
 import org.jmock.Expectations;
 
 import java.io.IOException;
@@ -31,7 +32,7 @@ import java.util.Set;
 public class BaseMavenInstallerTest extends AbstractMavenResolverTest {
     private BaseMavenInstaller mavenInstaller;
 
-    private InstallTaskFactory installTaskFactoryMock;
+    private Factory<CustomInstallTask> installTaskFactoryMock;
     private CustomInstallTask installTaskMock;
 
     protected BaseMavenInstaller createMavenInstaller() {
@@ -52,7 +53,7 @@ public class BaseMavenInstallerTest extends AbstractMavenResolverTest {
 
     public void setUp() {
         super.setUp();
-        installTaskFactoryMock = context.mock(InstallTaskFactory.class);
+        installTaskFactoryMock = context.mock(Factory.class);
         installTaskMock = context.mock(CustomInstallTask.class);
         mavenInstaller = createMavenInstaller();
         mavenInstaller.setInstallTaskFactory(installTaskFactoryMock);
@@ -61,7 +62,7 @@ public class BaseMavenInstallerTest extends AbstractMavenResolverTest {
     protected void checkTransaction(final Set<DeployableFilesInfo> deployableUnits, AttachedArtifact attachedArtifact, ClassifierArtifact classifierArtifact) throws IOException, PlexusContainerException {
         context.checking(new Expectations() {
             {
-                allowing(installTaskFactoryMock).createInstallTask();
+                allowing(installTaskFactoryMock).create();
                 will(returnValue(getInstallDeployTask()));
             }
         });
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java
index 0afc093..f488350 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/BasePomFilterContainerTest.java
@@ -18,7 +18,7 @@ package org.gradle.api.internal.artifacts.publish.maven.deploy;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.maven.MavenPom;
 import org.gradle.api.artifacts.maven.PublishFilter;
-import org.gradle.api.internal.artifacts.publish.maven.MavenPomFactory;
+import org.gradle.api.internal.Factory;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -40,7 +40,7 @@ public class BasePomFilterContainerTest {
     private static final String TEST_NAME = "testName";
     
     private BasePomFilterContainer pomFilterContainer;
-    protected MavenPomFactory mavenPomFactoryMock;
+    protected Factory<MavenPom> mavenPomFactoryMock;
     protected MavenPom pomMock;
     protected PomFilter pomFilterMock;
     protected PublishFilter publishFilterMock;
@@ -55,12 +55,12 @@ public class BasePomFilterContainerTest {
     @Before
     public void setUp() {
         pomFilterMock = context.mock(PomFilter.class);
-        mavenPomFactoryMock = context.mock(MavenPomFactory.class);
+        mavenPomFactoryMock = context.mock(Factory.class);
         pomMock = context.mock(MavenPom.class);
         publishFilterMock = context.mock(PublishFilter.class);
         context.checking(new Expectations() {
             {
-                allowing(mavenPomFactoryMock).createMavenPom();
+                allowing(mavenPomFactoryMock).create();
                 will(returnValue(pomMock));
             }
         });
@@ -77,12 +77,12 @@ public class BasePomFilterContainerTest {
 
     @Test(expected = InvalidUserDataException.class)
     public void getFilterWithNullName() {
-        pomFilterContainer.filter(null);
+        pomFilterContainer.filter((String) null);
     }
 
     @Test(expected = InvalidUserDataException.class)
     public void getPomWithNullName() {
-        pomFilterContainer.pom(null);
+        pomFilterContainer.pom((String) null);
     }
 
     @Test(expected = InvalidUserDataException.class)
@@ -92,7 +92,7 @@ public class BasePomFilterContainerTest {
 
     @Test(expected = InvalidUserDataException.class)
     public void addFilterWithNullFilter() {
-        pomFilterContainer.addFilter("somename", null);
+        pomFilterContainer.addFilter("somename", (PublishFilter) null);
     }
 
     @Test
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
index 908663a..0ebc7ce 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenDeployerTest.groovy
@@ -18,18 +18,19 @@ package org.gradle.api.internal.artifacts.publish.maven.deploy.groovy
 
 import java.lang.reflect.Proxy
 import org.gradle.api.artifacts.maven.MavenPom
+import org.gradle.api.artifacts.maven.PomFilterContainer
 import org.gradle.api.artifacts.maven.PublishFilter
 import org.gradle.api.internal.artifacts.publish.maven.deploy.BasePomFilterContainer
 import org.gradle.api.internal.artifacts.publish.maven.deploy.BasePomFilterContainerTest
 import org.hamcrest.BaseMatcher
 import org.hamcrest.Description
+import org.hamcrest.Factory
 import org.hamcrest.Matcher
 import org.jmock.integration.junit4.JMock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import static org.junit.Assert.assertSame
-import org.hamcrest.Factory
 
 /**
  * @author Hans Dockter
@@ -37,8 +38,7 @@ import org.hamcrest.Factory
 @RunWith(JMock)
 class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
     static final String TEST_NAME = "somename"
-    DefaultGroovyPomFilterContainer groovyPomFilterContainer
-
+    PomFilterContainer groovyPomFilterContainer
 
     @Before
     public void setUp() {
@@ -46,7 +46,7 @@ class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
     }
 
     protected BasePomFilterContainer createPomFilterContainer() {
-        return groovyPomFilterContainer = new DefaultGroovyPomFilterContainer(mavenPomFactoryMock);
+        return groovyPomFilterContainer = new BasePomFilterContainer(mavenPomFactoryMock);
     }
 
     @Test
@@ -68,7 +68,7 @@ class DefaultGroovyPomFilterContainerTest extends BasePomFilterContainerTest {
         context.checking {
             one(pomFilterMock).setFilter(withParam(FilterMatcher.equalsFilter(closureFilter)))
         }
-        MavenPom pom = groovyPomFilterContainer.filter(closureFilter)
+        groovyPomFilterContainer.filter(closureFilter)
     }
 
     @Test
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenInstallerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenInstallerTest.groovy
deleted file mode 100644
index 1928129..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenInstallerTest.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2007-2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-package org.gradle.api.internal.artifacts.publish.maven.deploy.groovy
-
-import org.gradle.api.artifacts.maven.GroovyPomFilterContainer
-import org.gradle.api.artifacts.maven.PomFilterContainer
-import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenInstaller
-import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenInstallerTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * @author Hans Dockter
- */
- at RunWith (org.jmock.integration.junit4.JMock.class)
-class DefaultGroovyMavenInstallerTest extends BaseMavenInstallerTest {
-
-    private DefaultGroovyMavenInstaller groovyMavenInstaller;
-
-    protected PomFilterContainer createPomFilterContainerMock() {
-        context.mock(GroovyPomFilterContainer.class);
-    }
-
-    protected BaseMavenInstaller createMavenInstaller() {
-        groovyMavenInstaller = new DefaultGroovyMavenInstaller(TEST_NAME, pomFilterContainerMock, artifactPomContainerMock, loggingManagerMock)
-    }
-
-    @Before
-    void setUp() {
-        super.setUp();
-    }
-
-    @Test
-    void filter() {
-        Closure testClosure = {}
-        context.checking {
-            one(pomFilterContainerMock).filter(testClosure)
-        }
-        groovyMavenInstaller.filter(testClosure)
-    }
-
-    @Test
-    void pom() {
-        Closure testClosure = {}
-        context.checking {
-            one(pomFilterContainerMock).pom(testClosure)
-        }
-        groovyMavenInstaller.pom(testClosure)
-    }
-
-    @Test
-    void pomWithName() {
-        Closure testClosure = {}
-        String testName = 'somename'
-        context.checking {
-            one(pomFilterContainerMock).pom(testName, testClosure)
-        }
-        groovyMavenInstaller.pom(testName, testClosure)
-    }
-
-    @Test
-    void addFilter() {
-        Closure testClosure = {}
-        String testName = 'somename'
-        context.checking {
-            one(pomFilterContainerMock).addFilter(testName, testClosure)
-        }
-        groovyMavenInstaller.addFilter(testName, testClosure)
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy
index 076cc35..5172ebd 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/artifacts/publish/maven/deploy/groovy/DefaultGroovyMavenUploaderTest.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.publish.maven.deploy.groovy
 
-import org.gradle.api.artifacts.maven.GroovyPomFilterContainer
 import org.gradle.api.artifacts.maven.PomFilterContainer
 import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployer
 import org.gradle.api.internal.artifacts.publish.maven.deploy.BaseMavenDeployerTest
@@ -34,7 +33,7 @@ class DefaultGroovyMavenDeployerTest extends BaseMavenDeployerTest {
     private DefaultGroovyPomFilterContainerTest groovyMavenResolverHelper
 
     protected PomFilterContainer createPomFilterContainerMock() {
-        context.mock(GroovyPomFilterContainer.class);
+        context.mock(PomFilterContainer.class);
     }
 
     protected BaseMavenDeployer createMavenDeployer() {
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/RelativePathTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/RelativePathTest.java
index e538d39..51cfde5 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/RelativePathTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/RelativePathTest.java
@@ -35,7 +35,7 @@ public class RelativePathTest {
 
     @Test
     public void testConstructors() {
-        RelativePath path, childPath;
+        RelativePath path;
         path = new RelativePath(true, "one");
         assertPathContains(path, true, "one");
 
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
index e2abc16..d4cf3d5 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/CopySpecImplTest.groovy
@@ -99,6 +99,15 @@ public class CopySpecImplTest {
         assertTrue(spec.sourcePaths.empty)
         assertThat(spec.childSpecs.size(), equalTo(2))
     }
+    
+    @Test public void testFromSpec() {
+        CopySpecImpl other1 = new CopySpecImpl(fileResolver)
+        CopySpecImpl other2 = new CopySpecImpl(fileResolver)
+
+        spec.from other1, other2
+        assertTrue(spec.sourcePaths.empty)
+        assertThat(spec.childSpecs.size(), equalTo(2))
+    }
 
     @Test public void testWithSpecSource() {
         CopyActionImpl source = new CopyActionImpl(fileResolver, null)
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java
index a156048..b4da117 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/MappingCopySpecVisitorTest.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.file.copy;
 
 import org.gradle.api.Action;
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.file.FileCopyDetails;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.RelativePath;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
index fe4f0e1..538e9bf 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/file/copy/SyncCopySpecVisitorTest.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.file.copy;
 
-import org.gradle.api.file.CopyAction;
 import org.gradle.api.file.FileVisitDetails;
 import org.gradle.api.file.RelativePath;
 import org.gradle.util.TestFile;
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy
index 7bb9961..767209f 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerFactoryTest.groovy
@@ -15,22 +15,22 @@
  */
 package org.gradle.api.internal.initialization
 
-import spock.lang.Specification
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory
+import org.gradle.api.artifacts.ConfigurationContainer
+import org.gradle.api.artifacts.dsl.RepositoryHandler
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory
 import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.api.artifacts.dsl.RepositoryHandler
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.util.ObservableUrlClassLoader
+import spock.lang.Specification
+import org.gradle.api.internal.Factory
 
 class DefaultScriptHandlerFactoryTest extends Specification {
-    private final RepositoryHandlerFactory repositoryHandlerFactory = Mock()
+    private final Factory<RepositoryHandler> repositoryHandlerFactory = Mock()
     private final ConfigurationContainerFactory configurationContainerFactory = Mock()
     private final DependencyMetaDataProvider metaDataProvider = Mock()
     private final DependencyFactory dependencyFactory = Mock()
-    private final ClassLoader parentClassLoader = Mock()
+    private final ClassLoader parentClassLoader = new ClassLoader() {}
     private final RepositoryHandler repositoryHandler = Mock()
     private final ConfigurationContainer configurationContainer = Mock()
     private final DefaultScriptHandlerFactory factory = new DefaultScriptHandlerFactory(repositoryHandlerFactory, configurationContainerFactory, metaDataProvider, dependencyFactory)
@@ -77,7 +77,7 @@ class DefaultScriptHandlerFactoryTest extends Specification {
     }
 
     private def expectConfigContainerCreated() {
-        _ * repositoryHandlerFactory.createRepositoryHandler(_) >> repositoryHandler
+        _ * repositoryHandlerFactory.create() >> repositoryHandler
         _ * configurationContainerFactory.createConfigurationContainer(repositoryHandler, metaDataProvider, _) >> configurationContainer
     }
 
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy
index 2d789ff..87a851b 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultAntBuilderFactoryTest.groovy
@@ -44,13 +44,13 @@ public class DefaultAntBuilderFactoryTest {
 
     @Test
     public void createsAnAntBuilder() {
-        def ant = factory.createAntBuilder()
+        def ant = factory.create()
         assertThat(ant, notNullValue())
     }
 
     @Test
     public void setsBaseDirOfAntProject() {
-        def ant = factory.createAntBuilder()
+        def ant = factory.create()
         assertThat(ant.project.baseDir, equalTo(project.projectDir))
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
index 2f859f0..9789fa7 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-
-
 package org.gradle.api.internal.project
 
 import java.awt.Point
@@ -26,20 +24,14 @@ import org.gradle.api.artifacts.Module
 import org.gradle.api.artifacts.dsl.ArtifactHandler
 import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.api.artifacts.dsl.RepositoryHandler
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory
-import org.gradle.api.artifacts.repositories.InternalRepository
 import org.gradle.api.initialization.dsl.ScriptHandler
-import org.gradle.api.internal.AsmBackedClassGenerator
-import org.gradle.api.internal.BeanDynamicObject
-import org.gradle.api.internal.ClassGenerator
-import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory
 import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
-import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandlerFactoryTest
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactFactory
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory
 import org.gradle.api.internal.artifacts.ivyservice.ResolverFactory
+import org.gradle.api.internal.artifacts.repositories.InternalRepository
 import org.gradle.api.internal.file.FileOperations
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider
@@ -66,6 +58,7 @@ import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.gradle.api.*
+import org.gradle.api.internal.*
 import static org.hamcrest.Matchers.*
 import static org.junit.Assert.*
 
@@ -99,14 +92,14 @@ class DefaultProjectTest {
     ServiceRegistry serviceRegistryMock
     ServiceRegistryFactory projectServiceRegistryFactoryMock
     TaskContainerInternal taskContainerMock
-    AntBuilderFactory antBuilderFactoryMock
+    Factory<AntBuilder> antBuilderFactoryMock
     AntBuilder testAntBuilder
 
     ConfigurationContainerFactory configurationContainerFactoryMock;
     DefaultConfigurationContainer configurationContainerMock;
     InternalRepository internalRepositoryDummy = context.mock(InternalRepository)
     ResolverFactory resolverFactoryMock = context.mock(ResolverFactory.class);
-    RepositoryHandlerFactory repositoryHandlerFactoryMock = context.mock(RepositoryHandlerFactory.class);
+    Factory<RepositoryHandler> repositoryHandlerFactoryMock = context.mock(Factory.class);
     RepositoryHandler repositoryHandlerMock
     DependencyFactory dependencyFactoryMock
     DependencyHandler dependencyHandlerMock = context.mock(DependencyHandler)
@@ -127,19 +120,19 @@ class DefaultProjectTest {
         dependencyFactoryMock = context.mock(DependencyFactory.class)
         loggingManagerMock = context.mock(LoggingManagerInternal.class)
         taskContainerMock = context.mock(TaskContainerInternal.class);
-        antBuilderFactoryMock = context.mock(AntBuilderFactory.class)
+        antBuilderFactoryMock = context.mock(Factory.class)
         testAntBuilder = new DefaultAntBuilder()
         context.checking {
-            allowing(antBuilderFactoryMock).createAntBuilder(); will(returnValue(testAntBuilder))
+            allowing(antBuilderFactoryMock).create(); will(returnValue(testAntBuilder))
         }
         configurationContainerMock = context.mock(DefaultConfigurationContainer.class)
         configurationContainerFactoryMock = [createConfigurationContainer: {
           resolverProvider, dependencyMetaDataProvider, projectDependenciesBuildInstruction ->
             assertSame(build.startParameter.projectDependenciesBuildInstruction, projectDependenciesBuildInstruction)
             configurationContainerMock}] as ConfigurationContainerFactory
-        repositoryHandlerMock =  context.mock(DefaultRepositoryHandlerFactoryTest.ConventionAwareRepositoryHandler.class);
+        repositoryHandlerMock =  context.mock(RepositoryHandler.class);
         context.checking {
-          allowing(repositoryHandlerFactoryMock).createRepositoryHandler(withParam(any(Convention))); will(returnValue(repositoryHandlerMock))
+          allowing(repositoryHandlerFactoryMock).create(); will(returnValue(repositoryHandlerMock))
         }
         script = context.mock(ScriptSource.class)
         context.checking {
@@ -162,16 +155,16 @@ class DefaultProjectTest {
 
         context.checking {
             allowing(projectServiceRegistryFactoryMock).createFor(withParam(notNullValue())); will(returnValue(serviceRegistryMock))
-            allowing(serviceRegistryMock).get(TaskContainerInternal); will(returnValue(taskContainerMock))
+            allowing(serviceRegistryMock).newInstance(TaskContainerInternal); will(returnValue(taskContainerMock))
             allowing(taskContainerMock).getAsDynamicObject(); will(returnValue(new BeanDynamicObject(new TaskContainerDynamicObject(someTask: testTask))))
             allowing(serviceRegistryMock).get(RepositoryHandler); will(returnValue(repositoryHandlerMock))
-            allowing(serviceRegistryMock).get(RepositoryHandlerFactory); will(returnValue(repositoryHandlerFactoryMock))
+            allowing(serviceRegistryMock).getFactory(RepositoryHandler); will(returnValue(repositoryHandlerFactoryMock))
             allowing(serviceRegistryMock).get(ConfigurationContainer); will(returnValue(configurationContainerMock))
             allowing(serviceRegistryMock).get(ArtifactHandler); will(returnValue(context.mock(ArtifactHandler)))
             allowing(serviceRegistryMock).get(DependencyHandler); will(returnValue(dependencyHandlerMock))
             allowing(serviceRegistryMock).get(Convention); will(returnValue(convention))
             allowing(serviceRegistryMock).get(ProjectEvaluator); will(returnValue(projectEvaluator))
-            allowing(serviceRegistryMock).get(AntBuilderFactory); will(returnValue(antBuilderFactoryMock))
+            allowing(serviceRegistryMock).getFactory(AntBuilder); will(returnValue(antBuilderFactoryMock))
             allowing(serviceRegistryMock).get(PluginContainer); will(returnValue(pluginContainerMock))
             allowing(serviceRegistryMock).get(ScriptHandler); will(returnValue(scriptHandlerMock))
             allowing(serviceRegistryMock).get(ScriptClassLoaderProvider); will(returnValue(context.mock(ScriptClassLoaderProvider)))
@@ -205,7 +198,7 @@ class DefaultProjectTest {
 
   @Test void testRepositories() {
       context.checking {
-          allowing(repositoryHandlerFactoryMock).createRepositoryHandler(withParam(any(Convention))); will(returnValue(repositoryHandlerMock))
+          allowing(repositoryHandlerFactoryMock).create(); will(returnValue(repositoryHandlerMock))
           ignoring(repositoryHandlerMock)
       }
       assertThat(project.createRepositoryHandler(), sameInstance(repositoryHandlerMock))
@@ -807,8 +800,9 @@ def scriptMethod(Closure closure) {
 
     @Test void testProperties() {
         context.checking {
-            allowing(dependencyMetaDataProviderMock).getModule(); will(returnValue([:] as Module))
+            allowing(dependencyMetaDataProviderMock).getModule(); will(returnValue({} as Module))
             ignoring(fileOperationsMock)
+            ignoring(taskContainerMock)
         }
         project.additional = 'additional'
 
@@ -1030,6 +1024,35 @@ def scriptMethod(Closure closure) {
         }
         assertThat(project.getModule(), equalTo(moduleDummyResolve))
     }
+
+    @Test void convertsAbsolutePathToAbsolutePath() {
+        assertThat(project.absoluteProjectPath(':'), equalTo(':'))
+        assertThat(project.absoluteProjectPath(':other'), equalTo(':other'))
+        assertThat(child1.absoluteProjectPath(':'), equalTo(':'))
+        assertThat(child1.absoluteProjectPath(':other'), equalTo(':other'))
+    }
+
+    @Test void convertsRelativePathToAbsolutePath() {
+        assertThat(project.absoluteProjectPath('task'), equalTo(':task'))
+        assertThat(project.absoluteProjectPath('sub:other'), equalTo(':sub:other'))
+        assertThat(child1.absoluteProjectPath('task'), equalTo(':child1:task'))
+        assertThat(child1.absoluteProjectPath('sub:other'), equalTo(':child1:sub:other'))
+    }
+
+    @Test void convertsRelativePathToRelativePath() {
+        assertThat(project.relativeProjectPath('task'), equalTo('task'))
+        assertThat(project.relativeProjectPath('sub:other'), equalTo('sub:other'))
+    }
+
+    @Test void convertsAbsolutePathToRelativePath() {
+        assertThat(project.relativeProjectPath(':'), equalTo(':'))
+        assertThat(project.relativeProjectPath(':task'), equalTo('task'))
+        assertThat(project.relativeProjectPath(':sub:other'), equalTo('sub:other'))
+        assertThat(child1.relativeProjectPath(':child1'), equalTo(':child1'))
+        assertThat(child1.relativeProjectPath(':child1:task'), equalTo('task'))
+        assertThat(child1.relativeProjectPath(':child12:task'), equalTo(':child12:task'))
+        assertThat(child1.relativeProjectPath(':sub:other'), equalTo(':sub:other'))
+    }
 }
 
 class TaskContainerDynamicObject {
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java
index 1d2925b..f989126 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/DefaultServiceRegistryTest.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.project;
 
+import org.gradle.api.internal.Factory;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -115,6 +116,70 @@ public class DefaultServiceRegistryTest {
     }
 
     @Test
+    public void canGetServiceAsFactoryWhenTheServiceImplementsFactoryInterface() {
+        assertThat(registry.getFactory(BigDecimal.class), instanceOf(TestFactory.class));
+        assertThat(registry.getFactory(BigDecimal.class), sameInstance((Object) registry.getFactory(BigDecimal.class)));
+    }
+
+    @Test
+    public void canLocateFactoryWhenServiceInterfaceExtendsFactory() {
+        registry.add(StringFactory.class, new StringFactory() {
+            public String create() {
+                return "value";
+            }
+        });
+        assertThat(registry.getFactory(String.class).create(), equalTo("value"));
+    }
+
+    @Test
+    public void usesAFactoryServiceToCreateInstances() {
+        assertThat(registry.newInstance(BigDecimal.class), equalTo(BigDecimal.valueOf(0)));
+        assertThat(registry.newInstance(BigDecimal.class), equalTo(BigDecimal.valueOf(1)));
+        assertThat(registry.newInstance(BigDecimal.class), equalTo(BigDecimal.valueOf(2)));
+    }
+
+    @Test
+    public void delegatesToParentForUnknownFactory() {
+        final Factory<Map> factory = context.mock(Factory.class);
+        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
+        TestRegistry registry = new TestRegistry(parent);
+
+        context.checking(new Expectations() {{
+            one(parent).getFactory(Map.class);
+            will(returnValue(factory));
+        }});
+
+        assertThat(registry.getFactory(Map.class), sameInstance((Object) factory));
+    }
+
+    @Test
+    public void usesDecoratorMethodToDecorateParentFactoryInstance() {
+        final ServiceRegistry parent = context.mock(ServiceRegistry.class);
+        final Factory<Long> factory = context.mock(Factory.class);
+        TestRegistry registry = new TestRegistry(parent);
+
+        context.checking(new Expectations() {{
+            one(parent).getFactory(Long.class);
+            will(returnValue(factory));
+            allowing(factory).create();
+            will(onConsecutiveCalls(returnValue(10L), returnValue(20L)));
+        }});
+
+        assertThat(registry.newInstance(Long.class), equalTo(12L));
+        assertThat(registry.newInstance(Long.class), equalTo(22L));
+    }
+    
+    @Test
+    public void throwsExceptionForUnknownFactory() {
+        try {
+            registry.getFactory(String.class);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage(), equalTo("No factory for objects of type String available in TestRegistry."));
+        }
+    }
+
+    @Test
     public void servicesCreatedByFactoryMethodsAreVisibleWhenUsingASubClass() {
         ServiceRegistry registry = new SubType();
         assertThat(registry.get(String.class), equalTo("12"));
@@ -146,7 +211,7 @@ public class DefaultServiceRegistryTest {
     }
 
     @Test
-    public void ignoresServiceWithNoCloseOrStopMethod() {
+    public void closeIgnoresServiceWithNoCloseOrStopMethod() {
         registry.add(String.class, "service");
 
         registry.close();
@@ -164,6 +229,18 @@ public class DefaultServiceRegistryTest {
         }
     }
 
+    @Test
+    public void discardsFactoriesOnClose() {
+        registry.getFactory(BigDecimal.class);
+        registry.close();
+        try {
+            registry.getFactory(BigDecimal.class);
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), equalTo("Cannot locate factory for objects of type BigDecimal, as TestRegistry has been closed."));
+        }
+    }
+
     private static class TestRegistry extends DefaultServiceRegistry {
         public TestRegistry() {
         }
@@ -183,11 +260,33 @@ public class DefaultServiceRegistryTest {
         protected Integer createInt() {
             return 12;
         }
+
+        protected Factory<BigDecimal> createTestFactory() {
+            return new TestFactory();
+        }
+
+        protected Factory<Long> createLongFactory(final Factory<? extends Long> factory) {
+            return new Factory<Long>() {
+                public Long create() {
+                    return factory.create() + 2;
+                }
+            };
+        }
     }
 
     private static class SubType extends TestRegistry {
     }
 
+    private static class TestFactory implements Factory<BigDecimal> {
+        int value;
+        public BigDecimal create() {
+            return BigDecimal.valueOf(value++);
+        }
+    }
+
+    private interface StringFactory extends Factory<String> {
+    }
+
     public interface TestCloseService {
         void close();
     }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java
index 2d588a6..ec5ac03 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GlobalServicesRegistryTest.java
@@ -23,12 +23,14 @@ import org.gradle.api.internal.GradleDistributionLocator;
 import org.gradle.cache.AutoCloseCacheFactory;
 import org.gradle.cache.CacheFactory;
 import org.gradle.initialization.ClassLoaderFactory;
-import org.gradle.initialization.CommandLine2StartParameterConverter;
+import org.gradle.initialization.CommandLineConverter;
 import org.gradle.initialization.DefaultClassLoaderFactory;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
+import org.gradle.initialization.DefaultCommandLineConverter;
 import org.gradle.listener.DefaultListenerManager;
 import org.gradle.listener.ListenerManager;
 import org.gradle.logging.*;
+import org.gradle.logging.internal.DefaultLoggingManagerFactory;
+import org.gradle.logging.internal.DefaultProgressLoggerFactory;
 import org.junit.Test;
 
 import static org.hamcrest.Matchers.*;
@@ -39,8 +41,8 @@ public class GlobalServicesRegistryTest {
 
     @Test
     public void providesCommandLineArgsConverter() {
-        assertThat(registry.get(CommandLine2StartParameterConverter.class), instanceOf(
-                DefaultCommandLine2StartParameterConverter.class));
+        assertThat(registry.get(CommandLineConverter.class), instanceOf(
+                DefaultCommandLineConverter.class));
     }
 
     @Test
@@ -60,7 +62,7 @@ public class GlobalServicesRegistryTest {
 
     @Test
     public void providesALoggingManagerFactory() {
-        assertThat(registry.get(LoggingManagerFactory.class), instanceOf(DefaultLoggingManagerFactory.class));
+        assertThat(registry.getFactory(LoggingManagerInternal.class), instanceOf(DefaultLoggingManagerFactory.class));
     }
     
     @Test
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.java
index bb577d6..419befc 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/GradleInternalServiceRegistryTest.java
@@ -16,36 +16,34 @@
 package org.gradle.api.internal.project;
 
 import org.gradle.StartParameter;
-import org.gradle.api.artifacts.repositories.InternalRepository;
 import org.gradle.api.execution.TaskExecutionGraphListener;
 import org.gradle.api.execution.TaskExecutionListener;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.PublishModuleDescriptorConverter;
 import org.gradle.api.internal.artifacts.repositories.DefaultInternalRepository;
+import org.gradle.api.internal.artifacts.repositories.InternalRepository;
 import org.gradle.api.internal.plugins.DefaultPluginRegistry;
 import org.gradle.api.internal.plugins.PluginRegistry;
 import org.gradle.execution.DefaultTaskGraphExecuter;
 import org.gradle.execution.TaskGraphExecuter;
 import org.gradle.listener.ListenerBroadcast;
 import org.gradle.listener.ListenerManager;
+import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.MultiParentClassLoader;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
 
 @RunWith(JMock.class)
 public class GradleInternalServiceRegistryTest {
-    private final JUnit4Mockery context = new JUnit4Mockery() {{
-        setImposteriser(ClassImposteriser.INSTANCE);    
-    }};
-    
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final GradleInternal gradle = context.mock(GradleInternal.class);
     private final ServiceRegistry parent = context.mock(ServiceRegistry.class);
     private final GradleInternalServiceRegistry registry = new GradleInternalServiceRegistry(parent, gradle);
@@ -56,7 +54,7 @@ public class GradleInternalServiceRegistryTest {
 
     @Before
     public void setUp() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             allowing(parent).get(PublishModuleDescriptorConverter.class);
             will(returnValue(publishModuleDescriptorConverter));
             allowing(parent).get(ListenerManager.class);
@@ -89,7 +87,7 @@ public class GradleInternalServiceRegistryTest {
 
     @Test
     public void providesATaskGraphExecuter() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(listenerManager).createAnonymousBroadcaster(TaskExecutionGraphListener.class);
             will(returnValue(new ListenerBroadcast<TaskExecutionGraphListener>(TaskExecutionGraphListener.class)));
             one(listenerManager).createAnonymousBroadcaster(TaskExecutionListener.class);
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java
index 9f85000..387cbdf 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectFactoryTest.java
@@ -18,16 +18,16 @@ package org.gradle.api.internal.project;
 
 import org.apache.commons.io.FileUtils;
 import org.gradle.StartParameter;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.initialization.ProjectDescriptor;
 import org.gradle.api.internal.ClassGenerator;
 import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.configurations.ResolverProvider;
 import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler;
-import org.gradle.api.plugins.Convention;
 import org.gradle.groovy.scripts.ScriptSource;
 import org.gradle.groovy.scripts.StringScriptSource;
 import org.gradle.groovy.scripts.UriScriptSource;
@@ -63,7 +63,7 @@ public class ProjectFactoryTest {
     private final File projectDir = new File(rootDir, "project");
     private ConfigurationContainerFactory configurationContainerFactory = context.mock(
             ConfigurationContainerFactory.class);
-    private RepositoryHandlerFactory repositoryHandlerFactory = context.mock(RepositoryHandlerFactory.class);
+    private Factory<RepositoryHandler> repositoryHandlerFactory = context.mock(Factory.class);
     private DefaultRepositoryHandler repositoryHandler = context.mock(DefaultRepositoryHandler.class);
     private StartParameter startParameterStub = new StartParameter();
     private ServiceRegistryFactory serviceRegistryFactory = new TopLevelBuildServiceRegistry(new GlobalServicesRegistry(), startParameterStub);
@@ -76,12 +76,12 @@ public class ProjectFactoryTest {
     public void setUp() throws Exception {
         startParameterStub.setGradleUserHomeDir(testDir.createDir("home"));
         context.checking(new Expectations() {{
-            allowing(repositoryHandlerFactory).createRepositoryHandler(with(any(Convention.class)));
+            allowing(repositoryHandlerFactory).create();
             will(returnValue(repositoryHandler));
         }});
         final ServiceRegistryFactory gradleServices = serviceRegistryFactory.createFor(gradle);
         context.checking(new Expectations() {{
-            allowing(gradle).getServiceRegistryFactory();
+            allowing(gradle).getServices();
             will(returnValue(gradleServices));
             allowing(gradle).getStartParameter();
             will(returnValue(startParameterStub));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java
index 2800b91..b7cb3f0 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/ProjectInternalServiceRegistryTest.java
@@ -16,20 +16,18 @@
 
 package org.gradle.api.internal.project;
 
+import org.gradle.api.AntBuilder;
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.dsl.ArtifactHandler;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.dsl.RepositoryHandler;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
 import org.gradle.api.initialization.dsl.ScriptHandler;
-import org.gradle.api.internal.AsmBackedClassGenerator;
-import org.gradle.api.internal.ClassGenerator;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.*;
 import org.gradle.api.internal.artifacts.ConfigurationContainerFactory;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler;
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactFactory;
+import org.gradle.api.internal.artifacts.dsl.SharedConventionRepositoryHandlerFactory;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory;
 import org.gradle.api.internal.file.*;
@@ -39,13 +37,13 @@ import org.gradle.api.internal.plugins.DefaultConvention;
 import org.gradle.api.internal.plugins.DefaultProjectsPluginContainer;
 import org.gradle.api.internal.plugins.PluginRegistry;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
-import org.gradle.api.internal.tasks.DefaultTaskContainer;
+import org.gradle.api.internal.tasks.DefaultTaskContainerFactory;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.PluginContainer;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -56,17 +54,17 @@ import org.junit.runner.RunWith;
 import java.io.File;
 
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
 
 @RunWith(JMock.class)
 public class ProjectInternalServiceRegistryTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
     private final ProjectInternal project = context.mock(ProjectInternal.class);
     private final ConfigurationContainer configurationContainer = context.mock(ConfigurationContainer.class);
     private final GradleInternal gradle = context.mock(GradleInternal.class);
     private final ConfigurationContainerFactory configurationContainerFactory = context.mock(
             ConfigurationContainerFactory.class);
-    private final RepositoryHandlerFactory repositoryHandlerFactory = context.mock(RepositoryHandlerFactory.class);
+    private final Factory<RepositoryHandler> repositoryHandlerFactory = context.mock(Factory.class);
     private final ITaskFactory taskFactory = context.mock(ITaskFactory.class);
     private final PublishArtifactFactory publishArtifactFactory = context.mock(PublishArtifactFactory.class);
     private final DependencyFactory dependencyFactory = context.mock(DependencyFactory.class);
@@ -84,7 +82,7 @@ public class ProjectInternalServiceRegistryTest {
             allowing(project).getBuildScriptSource();
             allowing(parent).get(ITaskFactory.class);
             will(returnValue(taskFactory));
-            allowing(parent).get(RepositoryHandlerFactory.class);
+            allowing(parent).getFactory(RepositoryHandler.class);
             will(returnValue(repositoryHandlerFactory));
             allowing(parent).get(ConfigurationContainerFactory.class);
             will(returnValue(configurationContainerFactory));
@@ -112,9 +110,8 @@ public class ProjectInternalServiceRegistryTest {
     }
 
     @Test
-    public void providesATaskContainer() {
-        assertThat(registry.get(TaskContainer.class), instanceOf(DefaultTaskContainer.class));
-        assertThat(registry.get(TaskContainer.class), sameInstance(registry.get(TaskContainer.class)));
+    public void providesATaskContainerFactory() {
+        assertThat(registry.getFactory(TaskContainerInternal.class), instanceOf(DefaultTaskContainerFactory.class));
     }
 
     @Test
@@ -129,16 +126,8 @@ public class ProjectInternalServiceRegistryTest {
     }
 
     @Test
-    public void providesARepositoryHandler() {
-        final RepositoryHandler repositoryHandler = context.mock(RepositoryHandler.class);
-
-        context.checking(new Expectations() {{
-            one(repositoryHandlerFactory).createRepositoryHandler(with(any(Convention.class)));
-            will(returnValue(repositoryHandler));
-        }});
-
-        assertThat(registry.get(RepositoryHandler.class), sameInstance(repositoryHandler));
-        assertThat(registry.get(RepositoryHandler.class), sameInstance(registry.get(RepositoryHandler.class)));
+    public void providesARepositoryHandlerFactory() {
+        assertThat(registry.getFactory(RepositoryHandler.class), instanceOf(SharedConventionRepositoryHandlerFactory.class));
     }
 
     @Test
@@ -159,8 +148,8 @@ public class ProjectInternalServiceRegistryTest {
 
     @Test
     public void providesAnAntBuilderFactory() {
-        assertThat(registry.get(AntBuilderFactory.class), instanceOf(DefaultAntBuilderFactory.class));
-        assertThat(registry.get(AntBuilderFactory.class), sameInstance(registry.get(AntBuilderFactory.class)));
+        assertThat(registry.getFactory(AntBuilder.class), instanceOf(DefaultAntBuilderFactory.class));
+        assertThat(registry.getFactory(AntBuilder.class), sameInstance((Object) registry.getFactory(AntBuilder.class)));
     }
 
     @Test
@@ -181,6 +170,10 @@ public class ProjectInternalServiceRegistryTest {
 
     @Test
     public void providesAFileOperationsInstance() {
+        context.checking(new Expectations(){{
+            one(project).getTasks();
+        }});
+
         assertThat(registry.get(FileOperations.class), instanceOf(DefaultFileOperations.class));
         assertThat(registry.get(FileOperations.class), sameInstance(registry.get(FileOperations.class)));
     }
@@ -193,11 +186,11 @@ public class ProjectInternalServiceRegistryTest {
     
     @Test
     public void providesALoggingManager() {
-        final LoggingManagerFactory loggingManagerFactory = context.mock(LoggingManagerFactory.class);
+        final Factory<LoggingManagerInternal> loggingManagerFactory = context.mock(Factory.class);
         final LoggingManager loggingManager = context.mock(LoggingManagerInternal.class);
 
         context.checking(new Expectations(){{
-            allowing(parent).get(LoggingManagerFactory.class);
+            allowing(parent).getFactory(LoggingManagerInternal.class);
             will(returnValue(loggingManagerFactory));
             one(loggingManagerFactory).create();
             will(returnValue(loggingManager));
@@ -223,14 +216,22 @@ public class ProjectInternalServiceRegistryTest {
 
     private void expectConfigurationHandlerCreated() {
         context.checking(new Expectations() {{
-            RepositoryHandler repositoryHandler = context.mock(RepositoryHandler.class);
+            RepositoryHandler repositoryHandler = context.mock(TestRepositoryHandler.class);
+
+            allowing(project).getRepositories();
+            will(returnValue(repositoryHandler));
 
-            one(repositoryHandlerFactory).createRepositoryHandler(with(notNullValue(Convention.class)));
+            allowing(repositoryHandlerFactory).create();
             will(returnValue(repositoryHandler));
 
+            ignoring(repositoryHandler);
+
             one(configurationContainerFactory).createConfigurationContainer(with(sameInstance(repositoryHandler)), with(
                     notNullValue(DependencyMetaDataProvider.class)), with(sameInstance(project)));
             will(returnValue(configurationContainer));
         }});
     }
+
+    private interface TestRepositoryHandler extends RepositoryHandler, IConventionAware {
+    }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java
index 65e3a0c..675c473 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TaskInternalServiceRegistryTest.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.project;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.TaskOutputsInternal;
 import org.gradle.api.internal.file.FileResolver;
@@ -23,7 +24,6 @@ import org.gradle.api.internal.tasks.DefaultTaskInputs;
 import org.gradle.api.internal.tasks.DefaultTaskOutputs;
 import org.gradle.api.logging.LoggingManager;
 import org.gradle.api.tasks.TaskInputs;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
@@ -32,8 +32,9 @@ import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertThat;
 
 @RunWith(JMock.class)
 public class TaskInternalServiceRegistryTest {
@@ -65,11 +66,11 @@ public class TaskInternalServiceRegistryTest {
     
     @Test
     public void createsALoggingManagerAndStdOutputCapture() {
-        final LoggingManagerFactory loggingManagerFactory = context.mock(LoggingManagerFactory.class);
+        final Factory<LoggingManagerInternal> loggingManagerFactory = context.mock(Factory.class);
         final LoggingManager loggingManager = context.mock(LoggingManagerInternal.class);
 
         context.checking(new Expectations(){{
-            allowing(parent).get(LoggingManagerFactory.class);
+            allowing(parent).getFactory(LoggingManagerInternal.class);
             will(returnValue(loggingManagerFactory));
             one(loggingManagerFactory).create();
             will(returnValue(loggingManager));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java
index c710aaf..3584517 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/internal/project/TopLevelBuildServiceRegistryTest.java
@@ -17,9 +17,10 @@
 package org.gradle.api.internal.project;
 
 import org.gradle.StartParameter;
-import org.gradle.api.artifacts.dsl.RepositoryHandlerFactory;
+import org.gradle.api.artifacts.dsl.RepositoryHandler;
 import org.gradle.api.internal.ClassPathRegistry;
 import org.gradle.api.internal.ExceptionAnalyser;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.dsl.DefaultPublishArtifactFactory;
 import org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandlerFactory;
@@ -29,6 +30,8 @@ import org.gradle.api.internal.tasks.TaskExecuter;
 import org.gradle.cache.CacheFactory;
 import org.gradle.cache.CacheRepository;
 import org.gradle.cache.DefaultCacheRepository;
+import org.gradle.configuration.BuildConfigurer;
+import org.gradle.configuration.DefaultBuildConfigurer;
 import org.gradle.configuration.DefaultScriptPluginFactory;
 import org.gradle.configuration.ScriptPluginFactory;
 import org.gradle.groovy.scripts.DefaultScriptCompilerFactory;
@@ -36,12 +39,12 @@ import org.gradle.groovy.scripts.ScriptCompilerFactory;
 import org.gradle.initialization.*;
 import org.gradle.listener.DefaultListenerManager;
 import org.gradle.listener.ListenerManager;
-import org.gradle.logging.LoggingManagerFactory;
+import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.logging.ProgressLoggerFactory;
 import org.gradle.messaging.concurrent.DefaultExecutorFactory;
 import org.gradle.messaging.concurrent.ExecutorFactory;
 import org.gradle.process.internal.DefaultWorkerProcessFactory;
-import org.gradle.process.internal.WorkerProcessFactory;
+import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.util.JUnit4GroovyMockery;
 import org.gradle.util.MultiParentClassLoader;
 import org.gradle.util.TemporaryFolder;
@@ -57,7 +60,8 @@ import java.io.File;
 import java.util.Collections;
 
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 @RunWith(JMock.class)
 public class TopLevelBuildServiceRegistryTest {
@@ -70,7 +74,7 @@ public class TopLevelBuildServiceRegistryTest {
     private final ClassPathRegistry classPathRegistry = context.mock(ClassPathRegistry.class);
     private final TopLevelBuildServiceRegistry factory = new TopLevelBuildServiceRegistry(parent, startParameter);
     private final ClassLoaderFactory classLoaderFactory = context.mock(ClassLoaderFactory.class);
-    private final LoggingManagerFactory loggingManagerFactory = context.mock(LoggingManagerFactory.class);
+    private final Factory<LoggingManagerInternal> loggingManagerFactory = context.mock(Factory.class);
     private final ProgressLoggerFactory progressLoggerFactory = context.mock(ProgressLoggerFactory.class);
 
     @Before
@@ -83,7 +87,7 @@ public class TopLevelBuildServiceRegistryTest {
             will(returnValue(classPathRegistry));
             allowing(parent).get(ClassLoaderFactory.class);
             will(returnValue(classLoaderFactory));
-            allowing(parent).get(LoggingManagerFactory.class);
+            allowing(parent).getFactory(LoggingManagerInternal.class);
             will(returnValue(loggingManagerFactory));
             allowing(parent).get(ProgressLoggerFactory.class);
             will(returnValue(progressLoggerFactory));
@@ -141,9 +145,7 @@ public class TopLevelBuildServiceRegistryTest {
 
     @Test
     public void providesARepositoryHandlerFactory() {
-        assertThat(factory.get(RepositoryHandlerFactory.class), instanceOf(DefaultRepositoryHandlerFactory.class));
-        assertThat(factory.get(RepositoryHandlerFactory.class), sameInstance(factory.get(
-                RepositoryHandlerFactory.class)));
+        assertThat(factory.getFactory(RepositoryHandler.class), instanceOf(DefaultRepositoryHandlerFactory.class));
     }
 
     @Test
@@ -198,8 +200,7 @@ public class TopLevelBuildServiceRegistryTest {
             }));
         }});
 
-        assertThat(factory.get(WorkerProcessFactory.class), instanceOf(DefaultWorkerProcessFactory.class));
-        assertThat(factory.get(WorkerProcessFactory.class), sameInstance(factory.get(WorkerProcessFactory.class)));
+        assertThat(factory.getFactory(WorkerProcessBuilder.class), instanceOf(DefaultWorkerProcessFactory.class));
     }
 
     @Test
@@ -214,6 +215,12 @@ public class TopLevelBuildServiceRegistryTest {
         assertThat(factory.get(ExecutorFactory.class), sameInstance(factory.get(ExecutorFactory.class)));
     }
 
+    @Test
+    public void providesABuildConfigurer() {
+        assertThat(factory.get(BuildConfigurer.class), instanceOf(DefaultBuildConfigurer.class));
+        assertThat(factory.get(BuildConfigurer.class), sameInstance(factory.get(BuildConfigurer.class)));
+    }
+
     private ListenerManager expectListenerManagerCreated() {
         final ListenerManager listenerManager = new DefaultListenerManager();
         context.checking(new Expectations(){{
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java
index 9fe918e..3bdd58b 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/DeleteTest.java
@@ -49,7 +49,7 @@ public class DeleteTest extends AbstractConventionTaskTest {
         super.setUp();
         delete = createTask(Delete.class);
         DefaultFileOperations fileOperations = (DefaultFileOperations) ((DefaultProject)
-                delete.getProject()).getServiceRegistryFactory().get(FileOperations.class);
+                delete.getProject()).getServices().get(FileOperations.class);
         fileOperations.setDeleteAction(deleteAction);
     }
 
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java
index 88d11c6..ba362e8 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AbstractReportTaskTest.java
@@ -17,7 +17,8 @@ package org.gradle.api.tasks.diagnostics;
 
 import org.gradle.api.Project;
 import org.gradle.api.internal.project.DefaultProject;
-import static org.gradle.util.HelperUtil.*;
+import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
+import org.gradle.logging.StyledTextOutput;
 import org.gradle.util.HelperUtil;
 import org.gradle.util.TemporaryFolder;
 import org.gradle.util.WrapUtil;
@@ -26,27 +27,31 @@ import org.jmock.Sequence;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.junit.Before;
-import org.junit.Test;
 import org.junit.Rule;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.IOException;
 
+import static org.gradle.util.HelperUtil.createChildProject;
+import static org.gradle.util.HelperUtil.createRootProject;
+import static org.hamcrest.Matchers.notNullValue;
+
 @RunWith(JMock.class)
 public class AbstractReportTaskTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
     private final DefaultProject project = createRootProject();
     private Runnable generator;
     private TestReportTask task;
-    private ProjectReportRenderer renderer;
+    private ReportRenderer renderer;
     @Rule
     public TemporaryFolder tmpDir = new TemporaryFolder();
 
     @Before
     public void setUp() throws Exception {
         generator = context.mock(Runnable.class);
-        renderer = context.mock(ProjectReportRenderer.class);
+        renderer = context.mock(ReportRenderer.class);
         task = HelperUtil.createTask(TestReportTask.class, project);
         task.setGenerator(generator);
         task.setRenderer(renderer);
@@ -57,6 +62,8 @@ public class AbstractReportTaskTest {
     public void completesRendererAtEndOfGeneration() throws IOException {
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("sequence");
+            one(renderer).setOutput((StyledTextOutput) with(notNullValue()));
+            inSequence(sequence);
             one(renderer).startProject(project);
             inSequence(sequence);
             one(generator).run();
@@ -100,6 +107,8 @@ public class AbstractReportTaskTest {
         context.checking(new Expectations() {{
             Sequence sequence = context.sequence("seq");
 
+            one(renderer).setOutput((StyledTextOutput) with(notNullValue()));
+            inSequence(sequence);
             one(renderer).startProject(project);
             inSequence(sequence);
             one(generator).run();
@@ -127,17 +136,17 @@ public class AbstractReportTaskTest {
 
     public static class TestReportTask extends AbstractReportTask {
         private Runnable generator;
-        private ProjectReportRenderer renderer;
+        private ReportRenderer renderer;
 
         public void setGenerator(Runnable generator) {
             this.generator = generator;
         }
 
-        public ProjectReportRenderer getRenderer() {
+        public ReportRenderer getRenderer() {
             return renderer;
         }
 
-        public void setRenderer(ProjectReportRenderer renderer) {
+        public void setRenderer(ReportRenderer renderer) {
             this.renderer = renderer;
         }
 
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AsciiReportRendererTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AsciiReportRendererTest.java
deleted file mode 100644
index 9a0a901..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/AsciiReportRendererTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import static org.junit.Assert.*;
-import org.gradle.api.Project;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.util.HelperUtil;
-import static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.Expectations;
-
-import java.io.StringWriter;
-
- at RunWith(JMock.class)
-public class AsciiReportRendererTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final StringWriter writer = new StringWriter();
-    private final AsciiReportRenderer renderer = new AsciiReportRenderer(writer);
-    private final Project project = HelperUtil.createRootProject();
-
-    @Test
-    public void writesMessageWhenProjectHasNoConfigurations() {
-        renderer.startProject(project);
-        renderer.completeProject(project);
-
-        assertThat(writer.toString(), containsLine("No configurations"));
-    }
-
-    @Test
-    public void writesConfigurationHeader() {
-        final Configuration configuration = context.mock(Configuration.class);
-        context.checking(new Expectations(){{
-            allowing(configuration).getName();
-            will(returnValue("configName"));
-            allowing(configuration).getDescription();
-            will(returnValue("description"));
-        }});
-
-        renderer.startProject(project);
-        renderer.startConfiguration(configuration);
-        renderer.completeConfiguration(configuration);
-        renderer.completeProject(project);
-
-        assertThat(writer.toString(), containsLine("configName - description"));
-        assertThat(writer.toString(), not(containsLine("No configurations")));
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskTest.java
index b5fe240..122aae6 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskTest.java
@@ -20,6 +20,8 @@ import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ResolvedConfiguration;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.tasks.diagnostics.internal.AsciiReportRenderer;
+import org.gradle.api.tasks.diagnostics.internal.DependencyReportRenderer;
 import org.gradle.util.WrapUtil;
 import org.gradle.util.HelperUtil;
 import static org.hamcrest.Matchers.instanceOf;
@@ -48,7 +50,7 @@ public class DependencyReportTaskTest {
         project = context.mock(ProjectInternal.class);
 
         context.checking(new Expectations() {{
-            allowing(project).absolutePath("list");
+            allowing(project).absoluteProjectPath("list");
             will(returnValue(":path"));
             allowing(project).getConvention();
             will(returnValue(null));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy
new file mode 100644
index 0000000..4a48fb7
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/ProjectReportTaskTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics
+
+import org.gradle.api.Project
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.logging.internal.TestStyledTextOutput
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+class ProjectReportTaskTest extends Specification {
+    private final ProjectInternal project = HelperUtil.createRootProject()
+
+    def rendersReportForRootProjectWithChildren() {
+        ProjectReportTask task = HelperUtil.createTask(ProjectReportTask, project)
+        project.description = 'this is the root project'
+        Project child1 = HelperUtil.createChildProject(project, "child1")
+        child1.description = 'this is a subproject'
+        HelperUtil.createChildProject(child1, "child1")
+        HelperUtil.createChildProject(project, "child2")
+        task.textOutput = new TestStyledTextOutput().ignoreStyle()
+
+        when:
+        task.listProjects()
+
+        then:
+        task.textOutput.value == '''
+Root project 'test' - this is the root project
++--- Project ':child1' - this is a subproject
+|    \\--- Project ':child1:child1'
+\\--- Project ':child2'
+
+To see a list of the tasks of a project, run gradle <project-path>:tasks
+For example, try running gradle :child1:tasks
+'''
+    }
+
+    def rendersReportForRootProjectWithNoChildren() {
+        ProjectReportTask task = HelperUtil.createTask(ProjectReportTask, project)
+        project.description = 'this is the root project'
+        task.textOutput = new TestStyledTextOutput().ignoreStyle()
+
+        when:
+        task.listProjects()
+
+        then:
+        task.textOutput.value == '''
+Root project 'test' - this is the root project
+No sub-projects
+
+To see a list of the tasks of a project, run gradle <project-path>:tasks
+For example, try running gradle :tasks
+'''
+    }
+
+    def rendersReportForNonRootProjectWithNoChildren() {
+        Project child1 = HelperUtil.createChildProject(project, "child1")
+        ProjectReportTask task = HelperUtil.createTask(ProjectReportTask, child1)
+        task.textOutput = new TestStyledTextOutput().ignoreStyle()
+
+        when:
+        task.listProjects()
+
+        then:
+        task.textOutput.value == '''
+Project ':child1'
+No sub-projects
+
+To see a list of the tasks of a project, run gradle <project-path>:tasks
+For example, try running gradle :child1:tasks
+
+To see a list of all the projects in this build, run gradle :projects
+'''
+    }
+
+    def String toNative(String value) {
+        return value.replaceAll('\n', System.getProperty('line.separator'))
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportRendererTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportRendererTest.java
deleted file mode 100644
index 164848d..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportRendererTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2008 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import static org.gradle.util.Matchers.*;
-import static org.junit.Assert.*;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.StringWriter;
-
-public class PropertyReportRendererTest {
-    private StringWriter out;
-    private PropertyReportRenderer renderer;
-
-    @Before
-    public void setUp() {
-        out = new StringWriter();
-        renderer = new PropertyReportRenderer(out);
-    }
-
-    @Test
-    public void writesProperty() {
-        renderer.addProperty("prop", "value");
-
-        assertThat(out.toString(), containsLine("prop: value"));
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTaskTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTaskTest.java
index 9372a36..4e6f89e 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTaskTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/PropertyReportTaskTest.java
@@ -16,6 +16,7 @@
 package org.gradle.api.tasks.diagnostics;
 
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.tasks.diagnostics.internal.PropertyReportRenderer;
 import org.gradle.util.GUtil;
 import org.gradle.util.HelperUtil;
 import org.jmock.Expectations;
@@ -43,7 +44,7 @@ public class PropertyReportTaskTest {
         renderer = context.mock(PropertyReportRenderer.class);
 
         context.checking(new Expectations() {{
-            allowing(project).absolutePath("list");
+            allowing(project).absoluteProjectPath("list");
             will(returnValue(":path"));
             allowing(project).getConvention();
             will(returnValue(null));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportModelTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportModelTest.groovy
deleted file mode 100644
index 0cca9aa..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportModelTest.groovy
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics
-
-import spock.lang.Specification
-import org.gradle.api.Task
-import org.gradle.api.tasks.TaskDependency
-
-class TaskReportModelTest extends Specification {
-    private final TaskReportModel model = new TaskReportModel()
-
-    def groupsTasksBasedOnTheirGroup() {
-        def task1 = task('task1', 'group1')
-        def task2 = task('task2', 'group2')
-        def task3 = task('task3', 'group1')
-
-        when:
-        model.calculate([task1, task2, task3])
-
-        then:
-        model.groups as List == ['group1', 'group2']
-        model.getTasksForGroup('group1')*.task == [task1, task3]
-        model.getTasksForGroup('group2')*.task == [task2]
-    }
-
-    def groupsAreTreatedAsCaseInsensitive() {
-        def task1 = task('task1', 'a')
-        def task2 = task('task2', 'B')
-        def task3 = task('task3', 'b')
-        def task4 = task('task4', 'c')
-
-        when:
-        model.calculate([task2, task3, task4, task1])
-
-        then:
-        model.groups as List == ['a', 'B', 'c']
-        model.getTasksForGroup('a')*.task == [task1]
-        model.getTasksForGroup('B')*.task == [task2, task3]
-        model.getTasksForGroup('c')*.task == [task4]
-    }
-
-    def tasksWithNoGroupAreTreatedAsChildrenOfTheNearestTopLevelTaskTheyAreReachableFrom() {
-        def task1 = task('task1')
-        def task2 = task('task2')
-        def task3 = task('task3', 'group1', task1, task2)
-        def task4 = task('task4', task3, task1)
-        def task5 = task('task5', 'group2', task4)
-
-        when:
-        model.calculate([task1, task2, task3, task4, task5])
-
-        then:
-        TaskDetails t = (model.getTasksForGroup('group1') as List).first()
-        t.task == task3
-        t.children*.task as List == [task1, task2]
-        t = (model.getTasksForGroup('group2') as List).first()
-        t.task == task5
-        t.children*.task as List == [task4]
-    }
-
-    def theDependenciesOfATopLevelTaskAreTheUnionOfItsChildrensDependencies() {
-        def task1 = task('task1', 'group1')
-        def task2 = task('task2', 'group2', task1)
-        def task3 = task('task3', 'group3')
-        def task4 = task('task4', task2)
-        def task5 = task('task5', 'group4', task3, task4)
-
-        when:
-        model.calculate([task1, task2, task3, task4, task5])
-
-        then:
-        TaskDetails t = (model.getTasksForGroup('group4') as List).first()
-        t.dependencies as List == [':task2', ':task3']
-    }
-    
-    def dependenciesDoNotIncludeTheChildrenOfOtherTopLevelTasks() {
-        def task1 = task('task1')
-        def task2 = task('task2', 'group1', task1)
-        def task3 = task('task3', task1)
-        def task4 = task('task4', task2)
-        def task5 = task('task5', 'group2', task3, task4)
-
-        when:
-        model.calculate([task1, task2, task3, task4, task5])
-
-        then:
-        TaskDetails t = (model.getTasksForGroup('group2') as List).first()
-        t.dependencies as List == [':task2']
-    }
-
-    def addsAGroupContainingTheTasksWithNoGroup() {
-        def task1 = task('task1')
-        def task2 = task('task2', 'group1', task1)
-        def task3 = task('task3')
-        def task4 = task('task4', task2)
-        def task5 = task('task5', task3, task4)
-
-        when:
-        model.calculate([task1, task2, task3, task4, task5])
-
-        then:
-        model.groups as List == ['group1', '']
-        def tasks = model.getTasksForGroup('') as List
-        tasks*.task == [task5]
-        def t = tasks.first()
-        t.task == task5
-        t.children*.task as List == [task3, task4]
-    }
-    
-    def addsAGroupWhenThereAreNoTasksWithAGroup() {
-        def task1 = task('task1')
-        def task2 = task('task2', task1)
-        def task3 = task('task3')
-
-        when:
-        model.calculate([task1, task2, task3])
-
-        then:
-        model.groups as List == ['']
-        def tasks = model.getTasksForGroup('') as List
-        tasks*.task == [task2, task3]
-    }
-
-    def ignoresReachableTasksOutsideTheProject() {
-        def other1 = task('other1')
-        def other2 = task('other2', other1)
-        def task1 = task('task1', other2)
-        def task2 = task('task2', 'group1', task1)
-
-        when:
-        model.calculate([task1, task2])
-
-        then:
-        TaskDetails t = (model.getTasksForGroup('group1') as List).first()
-        t.children*.task as List == [task1]
-        t.dependencies as List == [':other2']
-    }
-
-    def task(String name, String group = null, Task... dependencies) {
-        Task task = Mock()
-        _ * task.toString() >> name
-        _ * task.name >> name
-        _ * task.path >> ":$name"
-        _ * task.group >> group
-        _ * task.compareTo(!null) >> { args -> name.compareTo(args[0].name) }
-        TaskDependency dep = Mock()
-        _ * dep.getDependencies(task) >> {dependencies as Set}
-        _ * task.taskDependencies >> dep
-        return task
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportRendererTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportRendererTest.groovy
deleted file mode 100644
index 5b8eca3..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportRendererTest.groovy
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.diagnostics
-
-import org.gradle.api.Rule
-import org.junit.Test
-import static org.junit.Assert.*
-import groovy.io.PlatformLineWriter
-
-/**
- * @author Hans Dockter
- */
-class TaskReportRendererTest {
-    private final StringWriter writer = new StringWriter()
-    private final TaskReportRenderer renderer = new TaskReportRenderer(writer)
-
-    @Test public void testWritesTaskAndDependenciesWithNoDetail() {
-        TaskDetails task1 = [getPath: {':task1'}, getDescription: {'task1Description'}, getDependencies: {[':task11', ':task12'] as LinkedHashSet}] as TaskDetails
-        TaskDetails task2 = [getPath: {':task2'}, getDescription: {null}, getDependencies: {[] as Set}] as TaskDetails
-        TaskDetails task3 = [getPath: {':task3'}, getDescription: {null}, getDependencies: {[':task1'] as Set}] as TaskDetails
-        Rule rule1 = [getDescription: {'rule1Description'}] as Rule
-        Rule rule2 = [getDescription: {'rule2Description'}] as Rule
-
-        List testDefaultTasks = ['task1', 'task2']
-        renderer.showDetail(false)
-        renderer.addDefaultTasks(testDefaultTasks)
-        renderer.startTaskGroup('group')
-        renderer.addTask(task1)
-        renderer.addChildTask(task2)
-        renderer.addTask(task3)
-        renderer.completeTasks()
-        renderer.addRule(rule1)
-        renderer.addRule(rule2)
-
-        def expected = '''Default tasks: task1, task2
-
-Group tasks
------------
-:task1 - task1Description
-:task3
-
-Rules
------
-rule1Description
-rule2Description
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    @Test public void testWritesTaskAndDependenciesWithDetail() {
-        TaskDetails task1 = [getPath: {':task1'}, getDescription: {'task1Description'}, getDependencies: {[':task11', ':task12'] as LinkedHashSet}] as TaskDetails
-        TaskDetails task2 = [getPath: {':task2'}, getDescription: {null}, getDependencies: {[] as Set}] as TaskDetails
-        TaskDetails task3 = [getPath: {':task3'}, getDescription: {null}, getDependencies: {[':task1'] as Set}] as TaskDetails
-        Rule rule1 = [getDescription: {'rule1Description'}] as Rule
-        Rule rule2 = [getDescription: {'rule2Description'}] as Rule
-
-        List testDefaultTasks = ['task1', 'task2']
-        renderer.showDetail(true)
-        renderer.addDefaultTasks(testDefaultTasks)
-        renderer.startTaskGroup('group')
-        renderer.addTask(task1)
-        renderer.addChildTask(task2)
-        renderer.addTask(task3)
-        renderer.completeTasks()
-        renderer.addRule(rule1)
-        renderer.addRule(rule2)
-
-        def expected = '''Default tasks: task1, task2
-
-Group tasks
------------
-:task1 - task1Description [:task11, :task12]
-    :task2
-:task3 [:task1]
-
-Rules
------
-rule1Description
-rule2Description
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    @Test public void testWritesTasksForSingleGroup() {
-        TaskDetails task = [getPath: {':task1'}, getDescription: {null}, getDependencies: {[] as Set}] as TaskDetails
-        renderer.addDefaultTasks([])
-        renderer.startTaskGroup('group')
-        renderer.addTask(task)
-        renderer.completeTasks()
-
-        def expected = '''Group tasks
------------
-:task1
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    @Test public void testWritesTasksForMultipleGroups() {
-        TaskDetails task = [getPath: {':task1'}, getDescription: {null}, getDependencies: {[] as Set}] as TaskDetails
-        TaskDetails task2 = [getPath: {':task2'}, getDescription: {null}, getDependencies: {[] as Set}] as TaskDetails
-        renderer.addDefaultTasks([])
-        renderer.startTaskGroup('group')
-        renderer.addTask(task)
-        renderer.startTaskGroup('')
-        renderer.addTask(task2)
-        renderer.completeTasks()
-
-        def expected = '''Group tasks
------------
-:task1
-
-Other tasks
------------
-:task2
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    @Test public void testWritesTasksForDefaultGroup() {
-        TaskDetails task = [getPath: {':task1'}, getDescription: {null}, getDependencies: {[] as Set}] as TaskDetails
-        renderer.addDefaultTasks([])
-        renderer.startTaskGroup('')
-        renderer.addTask(task)
-        renderer.completeTasks()
-
-        def expected = '''Tasks
------
-:task1
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    @Test public void testProjectWithNoTasksAndNoRules() {
-        renderer.completeTasks()
-
-        def expected = '''No tasks
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    @Test public void testProjectWithRulesAndNoTasks() {
-        String ruleDescription = "someDescription"
-
-        renderer.completeTasks()
-        renderer.addRule([getDescription: {ruleDescription}] as Rule)
-
-        def expected = '''No tasks
-
-Rules
------
-someDescription
-'''
-        assertEquals(replaceWithPlatformNewLines(expected), writer.toString())
-    }
-
-    String replaceWithPlatformNewLines(String text) {
-        StringWriter stringWriter = new StringWriter()
-        new PlatformLineWriter(stringWriter).withWriter { it.write(text) }
-        stringWriter.toString()
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java
index 3c20f0d..dd2728a 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TaskReportTaskTest.java
@@ -15,13 +15,17 @@
  */
 package org.gradle.api.tasks.diagnostics;
 
-import org.gradle.api.Project;
 import org.gradle.api.Rule;
 import org.gradle.api.Task;
-import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.internal.project.AbstractProject;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.api.tasks.TaskDependency;
+import org.gradle.api.tasks.diagnostics.internal.TaskDetails;
+import org.gradle.api.tasks.diagnostics.internal.TaskReportRenderer;
 import org.gradle.util.HelperUtil;
 import org.gradle.util.JUnit4GroovyMockery;
+import org.gradle.util.Path;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
@@ -44,24 +48,27 @@ import static org.gradle.util.WrapUtil.*;
 @RunWith(JMock.class)
 public class TaskReportTaskTest {
     private final JUnit4Mockery context = new JUnit4GroovyMockery();
-    private TaskReportRenderer renderer;
-    private Project project;
+    private final TaskReportRenderer renderer = context.mock(TaskReportRenderer.class);
+    private final ProjectInternal project = context.mock(AbstractProject.class);
+    private final TaskContainerInternal taskContainer = context.mock(TaskContainerInternal.class);
+    private final TaskContainerInternal implicitTasks = context.mock(TaskContainerInternal.class);
     private TaskReportTask task;
-    private TaskContainer taskContainer;
 
     @Before
     public void setup() {
-        renderer = context.mock(TaskReportRenderer.class);
-        project = context.mock(Project.class);
-        taskContainer = context.mock(TaskContainer.class);
-
         context.checking(new Expectations(){{
-            allowing(project).absolutePath("list");
+            allowing(project).absoluteProjectPath("list");
             will(returnValue(":path"));
             allowing(project).getTasks();
             will(returnValue(taskContainer));
+            allowing(project).getImplicitTasks();
+            will(returnValue(implicitTasks));
             allowing(project).getConvention();
             will(returnValue(null));
+            allowing(project).getAllprojects();
+            will(returnValue(toSet(project)));
+            allowing(project).getSubprojects();
+            will(returnValue(toSet()));
         }});
 
         task = HelperUtil.createTask(TaskReportTask.class);
@@ -80,8 +87,11 @@ public class TaskReportTaskTest {
             allowing(project).getDefaultTasks();
             will(returnValue(testDefaultTasks));
 
-            one(taskContainer).getAll();
-            will(returnValue(toLinkedSet(task2, task3, task4, task1)));
+            one(taskContainer).iterator();
+            will(returnIterator(toLinkedSet(task2, task3, task4, task1)));
+
+            one(implicitTasks).iterator();
+            will(returnIterator(toLinkedSet()));
 
             allowing(taskContainer).getRules();
             will(returnValue(toList()));
@@ -129,8 +139,11 @@ public class TaskReportTaskTest {
             allowing(project).getDefaultTasks();
             will(returnValue(defaultTasks));
 
-            one(taskContainer).getAll();
-            will(returnValue(toSet()));
+            one(taskContainer).iterator();
+            will(returnIterator(toLinkedSet()));
+
+            one(implicitTasks).iterator();
+            will(returnIterator(toLinkedSet()));
 
             one(taskContainer).getRules();
             will(returnValue(toList(rule1, rule2)));
@@ -158,13 +171,11 @@ public class TaskReportTaskTest {
 
     private Matcher<TaskDetails> isTask(final Task task) {
         return new BaseMatcher<TaskDetails>() {
-            @Override
             public boolean matches(Object o) {
                 TaskDetails other = (TaskDetails) o;
-                return other.getPath().equals(task.getPath());
+                return other.getPath().equals(Path.path(task.getName()));
             }
 
-            @Override
             public void describeTo(Description description) {
                 description.appendText("is ").appendValue(task);
             }
@@ -182,17 +193,19 @@ public class TaskReportTaskTest {
             will(returnValue(name));
             allowing(task).getPath();
             will(returnValue(':' + name));
+            allowing(task).getProject();
+            will(returnValue(project));
+            allowing(project).relativeProjectPath(':' + name);
+            will(returnValue(name));
             allowing(task).getGroup();
             will(returnValue(taskGroup));
             allowing(task).compareTo(with(Matchers.notNullValue(Task.class)));
             will(new Action() {
-                @Override
                 public Object invoke(Invocation invocation) throws Throwable {
                     Task other = (Task) invocation.getParameter(0);
                     return name.compareTo(other.getName());
                 }
 
-                @Override
                 public void describeTo(Description description) {
                     description.appendText("compare to");
                 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TextProjectReportRendererTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TextProjectReportRendererTest.java
deleted file mode 100644
index 709f53e..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/TextProjectReportRendererTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.diagnostics;
-
-import org.gradle.api.Project;
-import org.gradle.util.TemporaryFolder;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-
-import static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
- at RunWith(JMock.class)
-public class TextProjectReportRendererTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    @Rule
-    public TemporaryFolder testDir = new TemporaryFolder();
-
-    @Test
-    public void writesReportToStandardOutByDefault() throws IOException {
-        TextProjectReportRenderer renderer = new TextProjectReportRenderer();
-        assertThat(renderer.getWriter(), sameInstance((Appendable) System.out));
-
-        renderer.complete();
-
-        assertThat(renderer.getWriter(), sameInstance((Appendable) System.out));
-    }
-
-    @Test
-    public void writesReportToAFile() throws IOException {
-        File outFile = new File(testDir.getDir(), "report.txt");
-        TextProjectReportRenderer renderer = new TextProjectReportRenderer();
-        renderer.setOutputFile(outFile);
-        assertThat(renderer.getWriter(), instanceOf(FileWriter.class));
-
-        renderer.complete();
-
-        assertTrue(outFile.isFile());
-        assertThat(renderer.getWriter(), sameInstance((Appendable) System.out));
-    }
-
-    @Test
-    public void writeRootProjectHeader() throws IOException {
-        final Project project = context.mock(Project.class);
-        StringWriter writer = new StringWriter();
-
-        context.checking(new Expectations() {{
-            allowing(project).getRootProject();
-            will(returnValue(project));
-        }});
-
-        TextProjectReportRenderer renderer = new TextProjectReportRenderer(writer);
-        renderer.startProject(project);
-        renderer.completeProject(project);
-        renderer.complete();
-
-        assertThat(writer.toString(), containsLine("Root Project"));
-    }
-    
-    @Test
-    public void writeSubProjectHeader() throws IOException {
-        final Project project = context.mock(Project.class);
-        StringWriter writer = new StringWriter();
-
-        context.checking(new Expectations() {{
-            allowing(project).getRootProject();
-            will(returnValue(context.mock(Project.class, "root")));
-            allowing(project).getPath();
-            will(returnValue("<path>"));
-        }});
-
-        TextProjectReportRenderer renderer = new TextProjectReportRenderer(writer);
-        renderer.startProject(project);
-        renderer.completeProject(project);
-        renderer.complete();
-
-        assertThat(writer.toString(), containsLine("Project <path>"));
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/AggregateMultiProjectTaskReportModelTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/AggregateMultiProjectTaskReportModelTest.groovy
new file mode 100644
index 0000000..a248575
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/AggregateMultiProjectTaskReportModelTest.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal
+
+class AggregateMultiProjectTaskReportModelTest extends TaskModelSpecification {
+    final AggregateMultiProjectTaskReportModel model = new AggregateMultiProjectTaskReportModel(false)
+
+    def mergesTheGroupsFromEachProject() {
+        TaskReportModel project1 = Mock()
+        TaskReportModel project2 = Mock()
+        _ * project1.groups >> (['p1', 'common'] as LinkedHashSet)
+        _ * project2.groups >> (['p2', 'common'] as LinkedHashSet)
+        _ * _.getTasksForGroup(_) >> ([taskDetails('task')] as Set)
+
+        when:
+        model.add(project1)
+        model.add(project2)
+        model.build()
+
+        then:
+        model.groups == ['p1', 'common', 'p2'] as Set
+    }
+
+    def mergesTheGroupsWithAGivenNameFromEachProjectIntoASingleGroup() {
+        TaskDetails task1 = taskDetails('task1')
+        TaskDetails task2 = taskDetails('task2')
+        TaskReportModel project1 = Mock()
+        TaskReportModel project2 = Mock()
+        _ * project1.groups >> (['group'] as LinkedHashSet)
+        _ * project1.getTasksForGroup('group') >> ([task1] as Set)
+        _ * project2.groups >> (['group'] as LinkedHashSet)
+        _ * project2.getTasksForGroup('group') >> ([task2] as Set)
+
+        when:
+        model.add(project1)
+        model.add(project2)
+        model.build()
+
+        then:
+        model.getTasksForGroup('group') == [task1, task2] as Set
+    }
+
+    def mergesTheTasksWithAGivenNameFromEachProjectIntoASingleTask() {
+        TaskDetails task1 = taskDetails(':task')
+        TaskDetails task2 = taskDetails(':other')
+        TaskDetails task3 = taskDetails(':sub:task')
+        TaskReportModel project1 = Mock()
+        TaskReportModel project2 = Mock()
+        _ * project1.groups >> (['group'] as LinkedHashSet)
+        _ * project1.getTasksForGroup('group') >> ([task1, task2] as Set)
+        _ * project2.groups >> (['group'] as LinkedHashSet)
+        _ * project2.getTasksForGroup('group') >> ([task3] as Set)
+
+        def model = new AggregateMultiProjectTaskReportModel(true)
+
+        when:
+        model.add(project1)
+        model.add(project2)
+        model.build()
+
+        then:
+        model.getTasksForGroup('group')*.path*.path as Set == ['task', 'other'] as Set
+    }
+
+    def handlesGroupWhichIsNotPresentInEachProject() {
+        TaskDetails task1 = taskDetails('task1')
+        TaskReportModel project1 = Mock()
+        TaskReportModel project2 = Mock()
+        _ * project1.groups >> (['group'] as LinkedHashSet)
+        _ * project1.getTasksForGroup('group') >> ([task1] as Set)
+        _ * project2.groups >> ([] as LinkedHashSet)
+        0 * project2.getTasksForGroup(_)
+
+        when:
+        model.add(project1)
+        model.add(project2)
+        model.build()
+
+        then:
+        model.getTasksForGroup('group') == [task1] as Set
+    }
+
+    def groupNamesAreCaseInsensitive() {
+        TaskDetails task1 = taskDetails('task1')
+        TaskDetails task2 = taskDetails('task2')
+        TaskReportModel project1 = Mock()
+        TaskReportModel project2 = Mock()
+        _ * project1.groups >> (['A'] as LinkedHashSet)
+        _ * project1.getTasksForGroup('A') >> ([task1] as Set)
+        _ * project2.groups >> (['a'] as LinkedHashSet)
+        _ * project2.getTasksForGroup('a') >> ([task2] as Set)
+
+        when:
+        model.add(project1)
+        model.add(project2)
+        model.build()
+
+        then:
+        model.groups == ['A'] as Set
+        model.getTasksForGroup('A') == [task1, task2] as Set
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/AsciiReportRendererTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/AsciiReportRendererTest.groovy
new file mode 100644
index 0000000..25a3c87
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/AsciiReportRendererTest.groovy
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.logging.internal.TestStyledTextOutput
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+import org.gradle.api.artifacts.ResolvedConfiguration
+import org.gradle.api.artifacts.ResolvedDependency
+
+class AsciiReportRendererTest extends Specification {
+    private final TestStyledTextOutput textOutput = new TestStyledTextOutput().ignoreStyle()
+    private final AsciiReportRenderer renderer = new AsciiReportRenderer()
+    private final Project project = HelperUtil.createRootProject()
+
+    def setup() {
+        renderer.output = textOutput
+    }
+
+    def writesMessageWhenProjectHasNoConfigurations() {
+        when:
+        renderer.startProject(project);
+        renderer.completeProject(project);
+
+        then:
+        textOutput.value.contains('No configurations')
+    }
+
+    def writesConfigurationHeader() {
+        Configuration configuration1 = Mock()
+        _ * configuration1.getName() >> 'config1'
+        _ * configuration1.getDescription() >> 'description'
+        Configuration configuration2 = Mock()
+        _ * configuration2.getName() >> 'config2'
+
+        when:
+        renderer.startConfiguration(configuration1);
+        renderer.completeConfiguration(configuration1);
+        renderer.startConfiguration(configuration2);
+        renderer.completeConfiguration(configuration2);
+
+        then:
+        textOutput.value.readLines() == [
+                'config1 - description',
+                '',
+                'config2'
+        ]
+    }
+
+    def rendersDependencyTreeForConfiguration() {
+        ResolvedConfiguration resolvedConfig = Mock()
+        Configuration configuration = Mock()
+        ResolvedDependency dep1 = Mock()
+        ResolvedDependency dep11 = Mock()
+        ResolvedDependency dep2 = Mock()
+        ResolvedDependency dep21 = Mock()
+        ResolvedDependency dep22 = Mock()
+        _ * configuration.name >> 'config'
+        _ * resolvedConfig.getFirstLevelModuleDependencies() >> {[dep1, dep2] as LinkedHashSet}
+        _ * dep1.getChildren() >> {[dep11] as LinkedHashSet}
+        _ * dep1.getName() >> 'dep1'
+        _ * dep1.getConfiguration() >> 'config1'
+        _ * dep11.getChildren() >> {[] as LinkedHashSet}
+        _ * dep11.getName() >> 'dep1.1'
+        _ * dep11.getConfiguration() >> 'config1.1'
+        _ * dep2.getChildren() >> {[dep21, dep22] as LinkedHashSet}
+        _ * dep2.getName() >> 'dep2'
+        _ * dep2.getConfiguration() >> 'config2'
+        _ * dep21.getChildren() >> {[] as LinkedHashSet}
+        _ * dep21.getName() >> 'dep2.1'
+        _ * dep21.getConfiguration() >> 'config2.1'
+        _ * dep22.getChildren() >> {[] as LinkedHashSet}
+        _ * dep22.getName() >> 'dep2.2'
+        _ * dep22.getConfiguration() >> 'config2.2'
+
+        when:
+        renderer.startConfiguration(configuration)
+        renderer.render(resolvedConfig)
+
+        then:
+        textOutput.value.readLines() == [
+                'config',
+                '+--- dep1 [config1]',
+                '|    \\--- dep1.1 [config1.1]',
+                '\\--- dep2 [config2]',
+                '     +--- dep2.1 [config2.1]',
+                '     \\--- dep2.2 [config2.2]'
+        ]
+    }
+
+    def rendersDependencyTreeForEmptyConfiguration() {
+        ResolvedConfiguration configuration = Mock()
+        _ * configuration.getFirstLevelModuleDependencies() >> {[] as Set}
+
+        when:
+        renderer.render(configuration)
+
+        then:
+        textOutput.value.readLines() == ['No dependencies']
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/DefaultGroupTaskReportModelTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/DefaultGroupTaskReportModelTest.groovy
new file mode 100644
index 0000000..ad28045
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/DefaultGroupTaskReportModelTest.groovy
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal
+
+class DefaultGroupTaskReportModelTest extends TaskModelSpecification {
+    final TaskReportModel target = Mock()
+    final DefaultGroupTaskReportModel model = new DefaultGroupTaskReportModel()
+
+    def mergesDefaultGroupIntoOtherGroup() {
+        TaskDetails task1 = taskDetails('1')
+        TaskDetails task2 = taskDetails('2')
+        TaskDetails task3 = taskDetails('3')
+        _ * target.groups >> ['a', '', 'other']
+        _ * target.getTasksForGroup('a') >> [task1]
+        _ * target.getTasksForGroup('') >> [task2]
+        _ * target.getTasksForGroup('other') >> [task3]
+        
+        when:
+        model.build(target)
+
+        then:
+
+        model.groups as List == ['a', 'other']
+        model.getTasksForGroup('a') as List == [task1]
+        model.getTasksForGroup('other') as List == [task2, task3]
+    }
+
+    def groupNamesAreOrderedCaseInsensitive() {
+        TaskDetails task1 = taskDetails('task1')
+        TaskDetails task2 = taskDetails('task2')
+        TaskDetails task3 = taskDetails('task3')
+        TaskDetails task4 = taskDetails('task4')
+        TaskDetails task5 = taskDetails('task5')
+
+        _ * target.groups >> (['Abc', 'a', 'A', '', 'Other'] as LinkedHashSet)
+        _ * target.getTasksForGroup('a') >> [task1]
+        _ * target.getTasksForGroup('A') >> [task2]
+        _ * target.getTasksForGroup('Abc') >> [task3]
+        _ * target.getTasksForGroup('') >> [task4]
+        _ * target.getTasksForGroup('Other') >> [task5]
+
+        when:
+        model.build(target)
+
+        then:
+        model.groups as List == ['A', 'a', 'Abc', 'Other']
+        model.getTasksForGroup('Other') as List == [task4, task5]
+    }
+
+    def taskNamesAreOrderedCaseInsensitiveByNameThenPath() {
+        def task1 = taskDetails('task_A')
+        def task2 = taskDetails('a:task_A')
+        def task3 = taskDetails('a:a:task_A')
+        def task4 = taskDetails('B:task_A')
+        def task5 = taskDetails('c:task_A')
+        def task6 = taskDetails('b:task_a')
+        def task7 = taskDetails('a:task_Abc')
+        def task8 = taskDetails('task_b')
+        _ * target.groups >> ['group']
+        _ * target.getTasksForGroup('group') >> ([task6, task3, task7, task4, task5, task1, task8, task2] as LinkedHashSet)
+
+        when:
+        model.build(target)
+
+        then:
+        model.getTasksForGroup('group') as List == [task1, task2, task3, task4, task5, task6, task7, task8]
+    }
+
+    def renamesDefaultGroupWhenOtherGroupNotPresent() {
+        TaskDetails task1 = taskDetails('1')
+        TaskDetails task2 = taskDetails('2')
+        _ * target.groups >> ['a', '']
+        _ * target.getTasksForGroup('a') >> [task1]
+        _ * target.getTasksForGroup('') >> [task2]
+
+        when:
+        model.build(target)
+
+        then:
+        model.groups as List == ['a', 'other']
+        model.getTasksForGroup('a') as List == [task1]
+        model.getTasksForGroup('other') as List == [task2]
+    }
+
+    def doesNotRenameDefaultGroupWhenItIsTheOnlyGroup() {
+        TaskDetails task1 = taskDetails('1')
+        _ * target.groups >> ['']
+        _ * target.getTasksForGroup('') >> [task1]
+
+        when:
+        model.build(target)
+
+        then:
+        model.groups as List == ['']
+        model.getTasksForGroup('') as List == [task1]
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/PropertyReportRendererTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/PropertyReportRendererTest.java
new file mode 100644
index 0000000..c1ab927
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/PropertyReportRendererTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.logging.internal.TestStyledTextOutput;
+import org.junit.Test;
+
+import static org.gradle.util.Matchers.containsLine;
+import static org.junit.Assert.assertThat;
+
+public class PropertyReportRendererTest {
+    private final TestStyledTextOutput out = new TestStyledTextOutput();
+    private final PropertyReportRenderer renderer = new PropertyReportRenderer(){{
+        setOutput(out);
+    }};
+
+    @Test
+    public void writesProperty() {
+        renderer.addProperty("prop", "value");
+
+        assertThat(out.toString(), containsLine("prop: value"));
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/SingleProjectTaskReportModelTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/SingleProjectTaskReportModelTest.groovy
new file mode 100644
index 0000000..ef8ad90
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/SingleProjectTaskReportModelTest.groovy
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal
+
+import org.gradle.util.Path
+
+class SingleProjectTaskReportModelTest extends TaskModelSpecification {
+    final TaskDetailsFactory factory = Mock()
+    final SingleProjectTaskReportModel model = new SingleProjectTaskReportModel(factory)
+
+    def setup() {
+        _ * factory.create(!null) >> {args ->
+            def task = args[0]
+            [getPath: { Path.path(task.path) }, getName: { task.name }] as TaskDetails
+        }
+    }
+
+    def groupsTasksBasedOnTheirGroup() {
+        def task1 = task('task1', 'group1')
+        def task2 = task('task2', 'group2')
+        def task3 = task('task3', 'group1')
+
+        when:
+        model.build([task1, task2, task3])
+
+        then:
+        model.groups == ['group1', 'group2'] as Set
+        model.getTasksForGroup('group1')*.task == [task1, task3]
+        model.getTasksForGroup('group2')*.task == [task2]
+    }
+
+    def groupsAreTreatedAsCaseInsensitive() {
+        def task1 = task('task1', 'a')
+        def task2 = task('task2', 'B')
+        def task3 = task('task3', 'b')
+        def task4 = task('task4', 'c')
+
+        when:
+        model.build([task2, task3, task4, task1])
+
+        then:
+        model.groups == ['a', 'B', 'c'] as Set
+        model.getTasksForGroup('a')*.task == [task1]
+        model.getTasksForGroup('B')*.task == [task2, task3]
+        model.getTasksForGroup('c')*.task == [task4]
+    }
+
+    def tasksWithNoGroupAreTreatedAsChildrenOfTheNearestTopLevelTaskTheyAreReachableFrom() {
+        def task1 = task('task1')
+        def task2 = task('task2')
+        def task3 = task('task3', 'group1', task1, task2)
+        def task4 = task('task4', task3, task1)
+        def task5 = task('task5', 'group2', task4)
+
+        when:
+        model.build([task1, task2, task3, task4, task5])
+
+        then:
+        TaskDetails t = (model.getTasksForGroup('group1') as List).first()
+        t.task == task3
+        t.children*.task == [task1, task2]
+        t = (model.getTasksForGroup('group2') as List).first()
+        t.task == task5
+        t.children*.task == [task4]
+    }
+
+    def theDependenciesOfATopLevelTaskAreTheUnionOfTheDependenciesOfItsChildren() {
+        def task1 = task('task1', 'group1')
+        def task2 = task('task2', 'group2', task1)
+        def task3 = task('task3', 'group3')
+        def task4 = task('task4', task2)
+        def task5 = task('task5', 'group4', task3, task4)
+
+        when:
+        model.build([task1, task2, task3, task4, task5])
+
+        then:
+        TaskDetails t = (model.getTasksForGroup('group4') as List).first()
+        t.dependencies*.path*.name as Set == ['task2', 'task3'] as Set
+    }
+
+    def dependenciesIncludeExternalTasks() {
+        def task1 = task('task1')
+        def task2 = task('task2', 'other')
+        def task3 = task('task3', 'group', task1, task2)
+
+        when:
+        model.build([task2, task3])
+
+        then:
+        TaskDetails t = (model.getTasksForGroup('group') as List).first()
+        t.dependencies*.path*.name as Set == ['task1', 'task2'] as Set
+    }
+
+    def dependenciesDoNotIncludeTheChildrenOfOtherTopLevelTasks() {
+        def task1 = task('task1')
+        def task2 = task('task2', 'group1', task1)
+        def task3 = task('task3', task1)
+        def task4 = task('task4', task2)
+        def task5 = task('task5', 'group2', task3, task4)
+
+        when:
+        model.build([task1, task2, task3, task4, task5])
+
+        then:
+        TaskDetails t = (model.getTasksForGroup('group2') as List).first()
+        t.dependencies*.path*.name as Set == ['task2'] as Set
+    }
+
+    def addsAGroupThatContainsTheTasksWithNoGroup() {
+        def task1 = task('task1')
+        def task2 = task('task2', 'group', task1)
+        def task3 = task('task3')
+        def task4 = task('task4', task2)
+        def task5 = task('task5', task3, task4)
+
+        when:
+        model.build([task1, task2, task3, task4, task5])
+
+        then:
+        model.groups == ['group', ''] as Set
+        def tasks = model.getTasksForGroup('') as List
+        tasks*.task == [task5]
+        def t = tasks.first()
+        t.task == task5
+        t.children*.task == [task3, task4]
+    }
+
+    def addsAGroupWhenThereAreNoTasksWithAGroup() {
+        def task1 = task('task1')
+        def task2 = task('task2', task1)
+        def task3 = task('task3')
+
+        when:
+        model.build([task1, task2, task3])
+
+        then:
+        model.groups == [''] as Set
+        def tasks = model.getTasksForGroup('') as List
+        tasks*.task == [task2, task3]
+    }
+
+    def buildsModelWhenThereAreNoTasks() {
+        when:
+        model.build([])
+
+        then:
+        model.groups as List == []
+    }
+
+    def ignoresReachableTasksOutsideTheProject() {
+        def other1 = task('other1')
+        def other2 = task('other2', other1)
+        def task1 = task('task1', other2)
+        def task2 = task('task2', 'group1', task1)
+
+        when:
+        model.build([task1, task2])
+
+        then:
+        TaskDetails t = (model.getTasksForGroup('group1') as List).first()
+        t.children*.task == [task1]
+        t.dependencies*.path*.name == ['other2']
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetailsFactoryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetailsFactoryTest.groovy
new file mode 100644
index 0000000..6a18b53
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskDetailsFactoryTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal
+
+import org.gradle.api.Task
+import org.gradle.api.Project
+import org.gradle.util.Path
+
+class TaskDetailsFactoryTest extends TaskModelSpecification {
+    final Project project = Mock()
+    final Project subproject = Mock()
+    final Task task = Mock()
+    TaskDetailsFactory factory
+
+    def setup() {
+        _ * project.allprojects >> ([project, subproject] as Set)
+        factory = new TaskDetailsFactory(project)
+    }
+    
+    def createsDetailsForTaskInMainProject() {
+        _ * task.project >> project
+        _ * task.path >> ':path'
+        _ * project.relativeProjectPath(':path') >> 'task'
+
+        expect:
+        def details = factory.create(task)
+        details.path == Path.path('task')
+    }
+
+    def createsDetailsForTaskInSubProject() {
+        _ * task.project >> subproject
+        _ * task.path >> ':sub:path'
+        _ * project.relativeProjectPath(':sub:path') >> 'sub:task'
+
+        expect:
+        def details = factory.create(task)
+        details.path == Path.path('sub:task')
+    }
+
+    def createsDetailsForTaskInOtherProject() {
+        Project other = Mock()
+        _ * task.project >> other
+        _ * task.path >> ':other:task'
+
+        expect:
+        def details = factory.create(task)
+        details.path == Path.path(':other:task')
+    }
+
+    def providesValuesForOtherProperties() {
+        _ * task.project >> project
+        _ * task.name >> 'task'
+        _ * task.description >> 'description'
+
+        expect:
+        def details = factory.create(task)
+        details.description == 'description'
+        details.dependencies.isEmpty()
+        details.children.isEmpty()
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskModelSpecification.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskModelSpecification.groovy
new file mode 100644
index 0000000..7a1cbe6
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskModelSpecification.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal
+
+import spock.lang.Specification
+import org.gradle.api.tasks.TaskDependency
+import org.gradle.api.Task
+import org.gradle.util.Path
+
+class TaskModelSpecification extends Specification {
+    def taskDetails(String path) {
+        return taskDetails([:], path)
+    }
+
+    def taskDetails(Map properties, String path) {
+        TaskDetails task = Mock()
+        _ * task.path >> Path.path(path)
+        _ * task.toString() >> path
+        _ * task.description >> properties.description
+        _ * task.dependencies >> ((properties.dependencies ?: []) as Set)
+        return task
+    }
+
+    def task(String name, String group = null, Task... dependencies) {
+        Task task = Mock()
+        _ * task.toString() >> name
+        _ * task.name >> name
+        _ * task.path >> ":$name"
+        _ * task.group >> group
+        _ * task.compareTo(!null) >> { args -> name.compareTo(args[0].name) }
+        TaskDependency dep = Mock()
+        _ * dep.getDependencies(task) >> {dependencies as Set}
+        _ * task.taskDependencies >> dep
+        return task
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRendererTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRendererTest.groovy
new file mode 100644
index 0000000..9e46523
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TaskReportRendererTest.groovy
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.diagnostics.internal
+
+import org.gradle.api.Rule
+import org.gradle.logging.internal.TestStyledTextOutput
+
+/**
+ * @author Hans Dockter
+ */
+class TaskReportRendererTest extends TaskModelSpecification {
+    private final TestStyledTextOutput writer = new TestStyledTextOutput().ignoreStyle()
+    private final TaskReportRenderer renderer = new TaskReportRenderer()
+
+    def setup() {
+        renderer.output = writer
+    }
+
+    def writesTaskAndDependenciesWithDetailDisabled() {
+        TaskDetails task1 = taskDetails(':task1', description: 'task1Description')
+        TaskDetails task2 = taskDetails(':task2')
+        TaskDetails task3 = taskDetails(':task3')
+        Rule rule1 = [getDescription: {'rule1Description'}] as Rule
+        Rule rule2 = [getDescription: {'rule2Description'}] as Rule
+
+        List testDefaultTasks = ['task1', 'task2']
+
+        when:
+        renderer.showDetail(false)
+        renderer.addDefaultTasks(testDefaultTasks)
+        renderer.startTaskGroup('group')
+        renderer.addTask(task1)
+        renderer.addChildTask(task2)
+        renderer.addTask(task3)
+        renderer.completeTasks()
+        renderer.addRule(rule1)
+        renderer.addRule(rule2)
+
+        then:
+        writer.value == '''Default tasks: task1, task2
+
+Group tasks
+-----------
+:task1 - task1Description
+:task3
+
+Rules
+-----
+rule1Description
+rule2Description
+'''
+    }
+
+    def writesTaskAndDependenciesWithDetail() {
+        TaskDetails task11 = taskDetails(':task11')
+        TaskDetails task12 = taskDetails(':task12')
+        TaskDetails task1 = taskDetails(':task1', description: 'task1Description', dependencies: [task11, task12])
+        TaskDetails task2 = taskDetails(':task2')
+        TaskDetails task3 = taskDetails(':task3', dependencies: [task1])
+        Rule rule1 = [getDescription: {'rule1Description'}] as Rule
+        Rule rule2 = [getDescription: {'rule2Description'}] as Rule
+        List testDefaultTasks = ['task1', 'task2']
+
+        when:
+        renderer.showDetail(true)
+        renderer.addDefaultTasks(testDefaultTasks)
+        renderer.startTaskGroup('group')
+        renderer.addTask(task1)
+        renderer.addChildTask(task2)
+        renderer.addTask(task3)
+        renderer.completeTasks()
+        renderer.addRule(rule1)
+        renderer.addRule(rule2)
+
+        then:
+        writer.value == '''Default tasks: task1, task2
+
+Group tasks
+-----------
+:task1 - task1Description [:task11, :task12]
+    :task2
+:task3 [:task1]
+
+Rules
+-----
+rule1Description
+rule2Description
+'''
+    }
+
+    def writesTasksForSingleGroup() {
+        TaskDetails task = taskDetails(':task1')
+
+        when:
+        renderer.addDefaultTasks([])
+        renderer.startTaskGroup('group')
+        renderer.addTask(task)
+        renderer.completeTasks()
+
+        then:
+        writer.value == '''Group tasks
+-----------
+:task1
+'''
+    }
+
+    def writesTasksForMultipleGroups() {
+        TaskDetails task = taskDetails(':task1')
+        TaskDetails task2 = taskDetails(':task2')
+
+        when:
+        renderer.addDefaultTasks([])
+        renderer.startTaskGroup('group')
+        renderer.addTask(task)
+        renderer.startTaskGroup('other')
+        renderer.addTask(task2)
+        renderer.completeTasks()
+
+        then:
+        writer.value == '''Group tasks
+-----------
+:task1
+
+Other tasks
+-----------
+:task2
+'''
+    }
+
+    def writesTasksForDefaultGroup() {
+        TaskDetails task = taskDetails(':task1')
+
+        when:
+        renderer.addDefaultTasks([])
+        renderer.startTaskGroup('')
+        renderer.addTask(task)
+        renderer.completeTasks()
+
+        then:
+        writer.value == '''Tasks
+-----
+:task1
+'''
+    }
+
+    def writesProjectWithNoTasksAndNoRules() {
+        when:
+        renderer.completeTasks()
+
+        then:
+        writer.value == '''No tasks
+'''
+    }
+
+    def writesProjectWithRulesAndNoTasks() {
+        String ruleDescription = "someDescription"
+
+        when:
+        renderer.completeTasks()
+        renderer.addRule([getDescription: {ruleDescription}] as Rule)
+
+        then:
+        writer.value == '''No tasks
+
+Rules
+-----
+someDescription
+'''
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRendererTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRendererTest.java
new file mode 100644
index 0000000..595146c
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/TextReportRendererTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.tasks.diagnostics.internal;
+
+import org.gradle.api.Project;
+import org.gradle.logging.internal.StreamingStyledTextOutput;
+import org.gradle.logging.internal.TestStyledTextOutput;
+import org.gradle.util.TemporaryFolder;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.gradle.util.Matchers.containsLine;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+ at RunWith(JMock.class)
+public class TextReportRendererTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    @Rule
+    public final TemporaryFolder testDir = new TemporaryFolder();
+    private final TextReportRenderer renderer = new TextReportRenderer();
+
+    @Test
+    public void writesReportToAFile() throws IOException {
+        File outFile = new File(testDir.getDir(), "report.txt");
+        renderer.setOutputFile(outFile);
+        assertThat(renderer.getTextOutput(), instanceOf(StreamingStyledTextOutput.class));
+
+        renderer.complete();
+
+        assertTrue(outFile.isFile());
+        assertThat(renderer.getTextOutput(), nullValue());
+    }
+
+    @Test
+    public void writeRootProjectHeader() throws IOException {
+        final Project project = context.mock(Project.class);
+        TestStyledTextOutput textOutput = new TestStyledTextOutput();
+
+        context.checking(new Expectations() {{
+            allowing(project).getRootProject();
+            will(returnValue(project));
+            allowing(project).getDescription();
+            will(returnValue(null));
+        }});
+
+        renderer.setOutput(textOutput);
+        renderer.startProject(project);
+        renderer.completeProject(project);
+        renderer.complete();
+
+        assertThat(textOutput.toString(), containsLine("Root Project"));
+    }
+
+    @Test
+    public void writeSubProjectHeader() throws IOException {
+        final Project project = context.mock(Project.class);
+        TestStyledTextOutput textOutput = new TestStyledTextOutput();
+
+        context.checking(new Expectations() {{
+            allowing(project).getRootProject();
+            will(returnValue(context.mock(Project.class, "root")));
+            allowing(project).getDescription();
+            will(returnValue(null));
+            allowing(project).getPath();
+            will(returnValue("<path>"));
+        }});
+
+        renderer.setOutput(textOutput);
+        renderer.startProject(project);
+        renderer.completeProject(project);
+        renderer.complete();
+
+        assertThat(textOutput.toString(), containsLine("Project <path>"));
+    }
+
+    @Test
+    public void includesProjectDescriptionInHeader() throws IOException {
+        final Project project = context.mock(Project.class);
+        TestStyledTextOutput textOutput = new TestStyledTextOutput();
+
+        context.checking(new Expectations() {{
+            allowing(project).getRootProject();
+            will(returnValue(project));
+            allowing(project).getDescription();
+            will(returnValue("this is the root project"));
+        }});
+
+        renderer.setOutput(textOutput);
+        renderer.startProject(project);
+        renderer.completeProject(project);
+        renderer.complete();
+
+        assertThat(textOutput.toString(), containsLine("Root Project - this is the root project"));
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/BuildConfigurerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/BuildConfigurerTest.java
deleted file mode 100644
index 2e17901..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/BuildConfigurerTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.configuration;
-
-import org.gradle.api.Action;
-import org.gradle.api.Project;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.util.HelperUtil;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(org.jmock.integration.junit4.JMock.class)
-public class BuildConfigurerTest {
-    BuildConfigurer buildConfigurer;
-    ProjectDependencies2TaskResolver projectDependencies2TasksResolver;
-    ProjectInternal rootProject;
-    boolean evaluatedCalled;
-    boolean resolveCalled;
-    SortedMap expectedTasksMap;
-    boolean expectedRecursive;
-
-    JUnit4Mockery context = new JUnit4Mockery();
-
-    @Before
-    public void setUp()  {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        projectDependencies2TasksResolver = context.mock(ProjectDependencies2TaskResolver.class);
-        buildConfigurer = new BuildConfigurer(projectDependencies2TasksResolver);
-        resolveCalled = false;
-        expectedTasksMap = new TreeMap();
-        rootProject = HelperUtil.createRootProject();
-        rootProject = context.mock(ProjectInternal.class);
-    }
-
-    private void createExpectations() {
-        final Action<Project> testEvaluateAction = new Action<Project>() {
-            public void execute(Project project) {
-            }
-        };
-        buildConfigurer.setProjectEvaluateAction(testEvaluateAction);
-        context.checking(new Expectations() {{
-            allowing(rootProject).evaluate(); will(returnValue(rootProject));
-            allowing(rootProject).getAllTasks(expectedRecursive); will(returnValue(expectedTasksMap));
-            one(rootProject).allprojects(testEvaluateAction);
-            one(projectDependencies2TasksResolver).resolve(with(same(rootProject)));
-        }});
-    }
-
-    @Test
-    public void testBuildConfigurer() {
-        assert buildConfigurer.getProjectDependencies2TasksResolver() == projectDependencies2TasksResolver;
-    }
-
-    @Test
-    public void testProcess() {
-        createExpectations();
-        buildConfigurer.process(rootProject);
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
new file mode 100644
index 0000000..f3c8a65
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultBuildConfigurerTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration
+
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.project.ProjectInternal
+import spock.lang.Specification
+import org.gradle.api.Action
+
+class DefaultBuildConfigurerTest extends Specification {
+    private final GradleInternal gradle = Mock()
+    private final ProjectInternal rootProject = Mock()
+    private final Action<? super ProjectInternal> action = Mock()
+    private final DefaultBuildConfigurer configurer = new DefaultBuildConfigurer(action)
+
+    def executesActionsForEachProject() {
+        when:
+        configurer.configure(gradle)
+
+        then:
+        _ * gradle.rootProject >> rootProject
+        1 * rootProject.allprojects(!null) >> { args ->
+            args[0].execute(rootProject)
+        }
+        1 * action.execute(rootProject)
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java
index 4cb4870..551136b 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/DefaultScriptPluginFactoryTest.java
@@ -15,12 +15,12 @@
  */
 package org.gradle.configuration;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.artifacts.dsl.ClasspathScriptTransformer;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
 import org.gradle.api.internal.initialization.ScriptHandlerInternal;
 import org.gradle.api.internal.project.ServiceRegistry;
 import org.gradle.groovy.scripts.*;
-import org.gradle.logging.LoggingManagerFactory;
 import org.gradle.logging.LoggingManagerInternal;
 import org.jmock.Expectations;
 import org.jmock.Sequence;
@@ -52,7 +52,7 @@ public class DefaultScriptPluginFactoryTest {
     private final ScriptHandlerInternal scriptHandlerMock = context.mock(ScriptHandlerInternal.class);
     private final ScriptRunner classPathScriptRunnerMock = context.mock(ScriptRunner.class, "classpathScriptRunner");
     private final BasicScript classPathScriptMock = context.mock(BasicScript.class, "classpathScript");
-    private final LoggingManagerFactory loggingManagerFactoryMock = context.mock(LoggingManagerFactory.class);
+    private final Factory<LoggingManagerInternal> loggingManagerFactoryMock = context.mock(Factory.class);
     private final DefaultScriptPluginFactory factory = new DefaultScriptPluginFactory(scriptCompilerFactoryMock, importsReaderMock, scriptHandlerFactoryMock, parentClassLoader, loggingManagerFactoryMock);
 
     @Test
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/GradleLauncherMetaDataTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/GradleLauncherMetaDataTest.groovy
new file mode 100644
index 0000000..58ea1a9
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/GradleLauncherMetaDataTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration
+
+import spock.lang.Specification
+import org.junit.Rule
+import org.gradle.util.SetSystemProperties
+
+class GradleLauncherMetaDataTest extends Specification {
+    @Rule public final SetSystemProperties sysProps = new SetSystemProperties()
+    private final GradleLauncherMetaData metaData = new GradleLauncherMetaData()
+
+    def usesSystemPropertyToDetermineLauncherName() {
+        System.setProperty("org.gradle.appname", "some-gradle-launcher")
+        def StringWriter writer = new StringWriter()
+
+        when:
+        metaData.describeCommand(writer, "[options]", "<task-name>")
+
+        then:
+        writer.toString() == "some-gradle-launcher [options] <task-name>"
+    }
+    
+    def usesDefaultValueWhenSystemPropertyNotSet() {
+        System.clearProperty("org.gradle.appname")
+        def StringWriter writer = new StringWriter()
+
+        when:
+        metaData.describeCommand(writer, "[options]", "<task-name>")
+
+        then:
+        writer.toString() == "gradle [options] <task-name>"
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ImplicitTasksConfigurerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ImplicitTasksConfigurerTest.groovy
new file mode 100644
index 0000000..f6e694d
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ImplicitTasksConfigurerTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.configuration
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.tasks.diagnostics.DependencyReportTask
+import org.gradle.api.tasks.diagnostics.ProjectReportTask
+import org.gradle.api.tasks.diagnostics.PropertyReportTask
+import org.gradle.api.tasks.diagnostics.TaskReportTask
+import org.gradle.util.HelperUtil
+import spock.lang.Specification
+
+class ImplicitTasksConfigurerTest extends Specification {
+    private final ImplicitTasksConfigurer configurer = new ImplicitTasksConfigurer()
+    private final ProjectInternal project = HelperUtil.createRootProject()
+
+    def addsImplicitTasksToProject() {
+        when:
+        configurer.execute(project)
+
+        then:
+        project.implicitTasks['help'] instanceof DefaultTask
+        project.implicitTasks['projects'] instanceof ProjectReportTask
+        project.implicitTasks['tasks'] instanceof TaskReportTask
+        project.implicitTasks['dependencies'] instanceof DependencyReportTask
+        project.implicitTasks['properties'] instanceof PropertyReportTask
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy
index da2caef..5a65ba8 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/configuration/ProjectDependencies2TaskResolverTest.groovy
@@ -44,7 +44,7 @@ class ProjectDependencies2TaskResolverTest {
 
     @Test public void testResolve() {
         child.dependsOn(root.path, false)
-        resolver.resolve(root)
+        resolver.execute(child)
         assertThat(childTask.taskDependencies.getDependencies(childTask), equalTo([rootTask] as Set))
     }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/BuiltInTaskBuildExecuterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/BuiltInTaskBuildExecuterTest.java
deleted file mode 100644
index e718ee8..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/BuiltInTaskBuildExecuterTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution;
-
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ServiceRegistryFactory;
-import org.gradle.api.internal.project.taskfactory.ITaskFactory;
-import org.gradle.api.tasks.diagnostics.AbstractReportTask;
-import org.gradle.api.tasks.diagnostics.TaskReportTask;
-import org.gradle.util.GUtil;
-import org.gradle.util.HelperUtil;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Collections;
-import java.util.Set;
-
-import static org.gradle.util.WrapUtil.toSet;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.Assert.assertThat;
-
- at RunWith(JMock.class)
-public class BuiltInTaskBuildExecuterTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final GradleInternal gradle = context.mock(GradleInternal.class);
-    private final ProjectInternal project = HelperUtil.createRootProject();
-    private final TaskGraphExecuter taskExecuter = context.mock(TaskGraphExecuter.class);
-    private final ITaskFactory taskFactory = context.mock(ITaskFactory.class);
-    private final BuiltInTaskBuildExecuter executer = new TestTaskBuildExecuter(null);
-    private final ServiceRegistryFactory serviceRegistryFactory = context.mock(ServiceRegistryFactory.class);
-
-    @Before
-    public void setUp() {
-        context.checking(new Expectations() {{
-            allowing(gradle).getDefaultProject();
-            will(returnValue(project));
-            allowing(gradle).getTaskGraph();
-            will(returnValue(taskExecuter));
-        }});
-    }
-
-    @Test
-    public void executesReportTask() {
-        expectTaskCreated();
-
-        executer.select(gradle);
-        assertThat(executer.getTask(), instanceOf(TaskReportTask.class));
-        assertThat(executer.getDisplayName(), equalTo("task list"));
-
-        expectTaskExecuted();
-
-        executer.execute();
-    }
-
-    @Test
-    public void executesAgainstDefaultProjectIfPathEmpty() {
-        expectTaskCreated();
-
-        executer.select(gradle);
-
-        assertThat(executer.getTask().getProjects(), equalTo(toSet((Project) gradle.getDefaultProject())));
-    }
-
-    @Test
-    public void executesAgainstSingleProjectSpecifiedByPath() {
-        String somePath = ":SomePath";
-        final ProjectInternal rootProject = context.mock(ProjectInternal.class, "rootProject");
-        final ProjectInternal someProject = context.mock(ProjectInternal.class, "someProject");
-
-        context.checking(new Expectations() {{
-            allowing(gradle).getRootProject();
-            will(returnValue(rootProject));
-            allowing(rootProject).project(":SomePath");
-            will(returnValue(someProject));
-        }});
-
-        BuiltInTaskBuildExecuter executer = new TestTaskBuildExecuter(somePath);
-
-        expectTaskCreated();
-        executer.select(gradle);
-
-        assertThat(executer.getTask().getProjects(), equalTo(toSet((Project) someProject)));
-    }
-
-    @Test
-    public void executesAgainstAllProjectWhenWildcardIsUsed() {
-        final ProjectInternal rootProject = context.mock(ProjectInternal.class, "rootProject");
-        final Set<Project> allProjects = toSet(context.mock(Project.class, "someProject"));
-        context.checking(new Expectations() {{
-            allowing(gradle).getRootProject();
-            will(returnValue(rootProject));
-            allowing(rootProject).getAllprojects();
-            will(returnValue(allProjects));
-        }});
-
-        BuiltInTaskBuildExecuter executer = new TestTaskBuildExecuter(BuiltInTaskBuildExecuter.ALL_PROJECTS_WILDCARD);
-
-        expectTaskCreated();
-
-        executer.select(gradle);
-        assertThat(executer.getTask().getProjects(), equalTo(allProjects));
-    }
-
-    private void expectTaskCreated() {
-        context.checking(new Expectations(){{
-            allowing(gradle).getServiceRegistryFactory();
-            will(returnValue(serviceRegistryFactory));
-
-            allowing(serviceRegistryFactory).get(ITaskFactory.class);
-            will(returnValue(taskFactory));
-
-            one(taskFactory).createTask(project, GUtil.map(Task.TASK_NAME, "report", Task.TASK_TYPE, TaskReportTask.class));
-            will(returnValue(HelperUtil.createTask(TaskReportTask.class)));
-        }});
-    }
-
-    private void expectTaskExecuted() {
-        context.checking(new Expectations() {{
-            one(taskExecuter).execute(Collections.singleton(executer.getTask()));
-        }});
-    }
-    
-    private class TestTaskBuildExecuter extends BuiltInTaskBuildExecuter {
-        private TestTaskBuildExecuter(String path) {
-            super(path);
-        }
-
-        @Override
-        protected Class<? extends AbstractReportTask> getTaskType() {
-            return TaskReportTask.class;
-        }
-
-        public String getDisplayName() {
-            return "task list";
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DefaultBuildExecuterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DefaultBuildExecuterTest.java
index 434e61e..0269772 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DefaultBuildExecuterTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DefaultBuildExecuterTest.java
@@ -18,14 +18,11 @@ package org.gradle.execution;
 import org.gradle.api.Task;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.api.specs.Spec;
-import static org.gradle.util.Matchers.*;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,6 +30,11 @@ import org.junit.runner.RunWith;
 import java.util.Collections;
 import java.util.List;
 
+import static org.gradle.util.WrapUtil.toList;
+import static org.gradle.util.WrapUtil.toSet;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.assertThat;
+
 @RunWith(JMock.class)
 public class DefaultBuildExecuterTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
@@ -60,7 +62,9 @@ public class DefaultBuildExecuterTest {
     public void usesNameResolvingExecuterWhenTaskNamesProvided() {
         List<String> taskNames = toList("a", "b");
         DefaultBuildExecuter executer = new DefaultBuildExecuter(taskNames, Collections.EMPTY_LIST);
-        assertThat(executer.getDelegate(), reflectionEquals((Object) new TaskNameResolvingBuildExecuter(taskNames)));
+        assertThat(executer.getDelegate(), instanceOf(TaskNameResolvingBuildExecuter.class));
+        TaskNameResolvingBuildExecuter delegate = (TaskNameResolvingBuildExecuter) executer.getDelegate();
+        assertThat(delegate.getNames(), equalTo(taskNames));
     }
 
     @Test
@@ -74,7 +78,9 @@ public class DefaultBuildExecuterTest {
     @Test
     public void usesNameResolvingExecuterAndNameFilterWhenTaskNamesAndExcludePatternsProvided() {
         DefaultBuildExecuter executer = new DefaultBuildExecuter(toList("a"), toList("b"));
-        assertThat(executer.getDelegate(), reflectionEquals((Object) new TaskNameResolvingBuildExecuter(toList("a"))));
+        assertThat(executer.getDelegate(), instanceOf(TaskNameResolvingBuildExecuter.class));
+        TaskNameResolvingBuildExecuter delegate = (TaskNameResolvingBuildExecuter) executer.getDelegate();
+        assertThat(delegate.getNames(), equalTo(toList("a")));
 
         checkNameFilterApplied(executer);
     }
@@ -84,8 +90,16 @@ public class DefaultBuildExecuterTest {
         executer.setDelegate(delegate);
 
         context.checking(new Expectations(){{
-            one(project).getTasksByName("b", true);
-            will(returnValue(toSet(context.mock(Task.class))));
+            Task task = context.mock(Task.class);
+            TaskContainerInternal taskContainer = context.mock(TaskContainerInternal.class);
+            allowing(project).getSubprojects();
+            will(returnValue(toSet()));
+            allowing(project).getTasks();
+            will(returnValue(taskContainer));
+            one(taskContainer).findByName("b");
+            will(returnValue(task));
+            allowing(task).getName();
+            will(returnValue("b"));
             one(taskExecuter).useFilter(with(notNullValue(Spec.class)));
             one(delegate).select(gradle);
         }});
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DependencyReportBuildExecuterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DependencyReportBuildExecuterTest.groovy
deleted file mode 100644
index 6f05853..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/DependencyReportBuildExecuterTest.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution
-
-import org.gradle.api.tasks.diagnostics.DependencyReportTask
-import spock.lang.Specification
-
-class DependencyReportBuildExecuterTest extends Specification {
-    private final DependencyReportBuildExecuter executer = new DependencyReportBuildExecuter("path")
-    
-    def constructsADependencyReportTask() {
-        expect:
-        executer.taskType == DependencyReportTask
-        executer.displayName == 'dependency list'
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/ProjectDefaultsBuildExecuterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/ProjectDefaultsBuildExecuterTest.java
index 1bf0acc..d806d7e 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/ProjectDefaultsBuildExecuterTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/ProjectDefaultsBuildExecuterTest.java
@@ -17,16 +17,17 @@ package org.gradle.execution;
 
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.util.Matchers;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import static org.gradle.util.WrapUtil.toList;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
 @RunWith (org.jmock.integration.junit4.JMock.class)
 public class ProjectDefaultsBuildExecuterTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
@@ -49,7 +50,10 @@ public class ProjectDefaultsBuildExecuterTest {
 
         TestProjectDefaultsBuildExecuter executer = new TestProjectDefaultsBuildExecuter();
         executer.select(gradle);
-        assertThat(executer.actualDelegate, Matchers.reflectionEquals((Object) new TaskNameResolvingBuildExecuter(toList("a", "b"))));
+
+        assertThat(executer.actualDelegate, instanceOf(TaskNameResolvingBuildExecuter.class));
+        TaskNameResolvingBuildExecuter delegate = (TaskNameResolvingBuildExecuter) executer.actualDelegate;
+        assertThat(delegate.getNames(), equalTo(toList("a", "b")));
     }
 
     @Test public void createsDescription() {
@@ -63,19 +67,20 @@ public class ProjectDefaultsBuildExecuterTest {
         assertThat(executer.getDisplayName(), equalTo("project default tasks 'a', 'b'"));
     }
 
-    @Test public void failsWhenNoProjectDefaultTasksSpecified() {
+    @Test public void usesHelpTaskWhenProjectHasNoDefaultTasks() {
         context.checking(new Expectations() {{
             one(project).getDefaultTasks();
             will(returnValue(toList()));
         }});
 
-        BuildExecuter executer = new ProjectDefaultsBuildExecuter();
-        try {
-            executer.select(gradle);
-            fail();
-        } catch (TaskSelectionException e) {
-            assertThat(e.getMessage(), equalTo("No tasks have been specified and [project] has not defined any default tasks."));
-        }
+        TestProjectDefaultsBuildExecuter executer = new TestProjectDefaultsBuildExecuter();
+        executer.select(gradle);
+
+        assertThat(executer.actualDelegate, instanceOf(TaskNameResolvingBuildExecuter.class));
+        TaskNameResolvingBuildExecuter delegate = (TaskNameResolvingBuildExecuter) executer.actualDelegate;
+        assertThat(delegate.getNames(), equalTo(toList("help")));
+
+        assertThat(executer.getDisplayName(), equalTo("default task 'help'"));
     }
 
     private static class TestProjectDefaultsBuildExecuter extends ProjectDefaultsBuildExecuter {
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/PropertyReportBuildExecuterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/PropertyReportBuildExecuterTest.groovy
deleted file mode 100644
index 74196cd..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/PropertyReportBuildExecuterTest.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution
-
-import org.gradle.api.tasks.diagnostics.PropertyReportTask
-import spock.lang.Specification
-
-class PropertyReportBuildExecuterTest extends Specification {
-    private final PropertyReportBuildExecuter executer = new PropertyReportBuildExecuter("path")
-    
-    def constructsAPropertyReportTask() {
-        expect:
-        executer.taskType == PropertyReportTask
-        executer.displayName == 'property list'
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
new file mode 100644
index 0000000..1f56ba4
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolverTest.groovy
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.execution
+
+import org.gradle.api.Task
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.tasks.TaskContainerInternal
+import spock.lang.Specification
+
+class TaskNameResolverTest extends Specification {
+    private final TaskNameResolver resolver = new TaskNameResolver()
+
+    def selectsTaskForSingleProjectWhenThereIsAnExactMatchOnName() {
+        ProjectInternal project = Mock()
+        TaskContainerInternal tasks = Mock()
+        _ * project.tasks >> tasks
+
+        Task task = task('task')
+
+        when:
+        def candidates = resolver.select('task', project)
+
+        then:
+        1 * tasks.findByName('task') >> task
+        candidates.get('task') == [task] as Set
+    }
+
+    def selectsImplicitTaskForSingleProjectWhenThereIsAnExactMatchOnName() {
+        ProjectInternal project = Mock()
+        TaskContainerInternal tasks = Mock()
+        TaskContainerInternal implicitTasks = Mock()
+        _ * project.tasks >> tasks
+        _ * project.implicitTasks >> implicitTasks
+
+        Task task = task('task')
+
+        when:
+        def candidates = resolver.select('task', project)
+
+        then:
+        1 * tasks.findByName('task') >> null
+        1 * implicitTasks.findByName('task') >> task
+        candidates.get('task') == [task] as Set
+    }
+
+    def selectsAllTasksForSingleProjectWhenThereIsNoExactMatchOnName() {
+        ProjectInternal project = Mock()
+        TaskContainerInternal tasks = Mock()
+        TaskContainerInternal implicitTasks = Mock()
+        _ * project.tasks >> tasks
+        _ * project.implicitTasks >> implicitTasks
+
+        Task task1 = task('task1')
+        Task task2 = task('task2')
+        Task hidden = task('task1')
+
+        when:
+        def candidates = resolver.select('task', project)
+
+        then:
+        1 * tasks.findByName('task') >> null
+        1 * implicitTasks.findByName('task') >> null
+        1 * tasks.iterator() >> [task1].iterator()
+        1 * implicitTasks.iterator() >> [task2, hidden].iterator()
+        candidates.get('task1') == [task1] as Set
+        candidates.get('task2') == [task2] as Set
+    }
+
+    def selectsTasksForMultipleProjectsWhenThereIsAnExactMatchOnName() {
+        ProjectInternal project = Mock()
+        TaskContainerInternal tasks = Mock()
+        ProjectInternal childProject = Mock()
+        TaskContainerInternal childProjectTasks = Mock()
+        _ * project.tasks >> tasks
+        _ * project.subprojects >> ([childProject] as Set)
+        _ * childProject.tasks >> childProjectTasks
+
+        Task task1 = task('task')
+        Task task2 = task('task')
+
+        when:
+        def candidates = resolver.selectAll('task', project)
+
+        then:
+        1 * tasks.findByName('task') >> task1
+        1 * childProjectTasks.findByName('task') >> task2
+        candidates.get('task') == [task1, task2] as Set
+    }
+
+    def selectsImplicitTaskForMultipleProjectsWhenThereIsAnExactMatchOnName() {
+        ProjectInternal project = Mock()
+        TaskContainerInternal tasks = Mock()
+        TaskContainerInternal implicitTasks = Mock()
+        ProjectInternal childProject = Mock()
+        TaskContainerInternal childProjectTasks = Mock()
+        _ * project.tasks >> tasks
+        _ * project.implicitTasks >> implicitTasks
+        _ * project.subprojects >> ([childProject] as Set)
+        _ * childProject.tasks >> childProjectTasks
+
+        Task task1 = task('task')
+        Task task2 = task('task')
+
+        when:
+        def candidates = resolver.selectAll('task', project)
+
+        then:
+        1 * tasks.findByName('task') >> null
+        1 * implicitTasks.findByName('task') >> task1
+        1 * childProjectTasks.findByName('task') >> task2
+        candidates.get('task') == [task1, task2] as Set
+    }
+
+    def selectsAllTasksForMultipleProjectsWhenThereIsNoExactMatchOnName() {
+        ProjectInternal project = Mock()
+        TaskContainerInternal tasks = Mock()
+        TaskContainerInternal implicitTasks = Mock()
+        ProjectInternal childProject = Mock()
+        TaskContainerInternal childProjectTasks = Mock()
+        _ * project.tasks >> tasks
+        _ * project.implicitTasks >> implicitTasks
+        _ * project.subprojects >> ([childProject] as Set)
+        _ * childProject.tasks >> childProjectTasks
+
+        Task task1 = task('name1')
+        Task task2 = task('name2')
+        Task task3 = task('name1')
+        Task task4 = task('name2')
+
+        when:
+        def candidates = resolver.selectAll('task', project)
+
+        then:
+        1 * tasks.findByName('task') >> null
+        1 * implicitTasks.findByName('task') >> null
+        1 * childProjectTasks.findByName('task') >> null
+        1 * tasks.iterator() >> [task1].iterator()
+        1 * implicitTasks.iterator() >> [task2].iterator()
+        1 * childProjectTasks.iterator() >> [task3, task4].iterator()
+        candidates.get('name1') == [task1, task3] as Set
+        candidates.get('name2') == [task2, task4] as Set
+    }
+
+    def task(String name) {
+        Task task = Mock()
+        _ * task.name >> name
+        return task
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java
index defc675..b314e43 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskNameResolvingBuildExecuterTest.java
@@ -15,34 +15,38 @@
  */
 package org.gradle.execution;
 
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
 import org.gradle.api.Task;
 import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.project.AbstractProject;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.TaskContainerInternal;
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
+import org.gradle.api.internal.tasks.CommandLineOption;
 import org.gradle.util.GUtil;
+import org.gradle.util.JUnit4GroovyMockery;
 import org.jmock.Expectations;
 import org.jmock.Sequence;
 import org.jmock.integration.junit4.JUnit4Mockery;
-import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
+
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 @RunWith (org.jmock.integration.junit4.JMock.class)
 public class TaskNameResolvingBuildExecuterTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private final ProjectInternal project = context.mock(ProjectInternal.class, "[project]");
-    private final ProjectInternal otherProject = context.mock(ProjectInternal.class, "[otherProject]");
+    private final JUnit4Mockery context = new JUnit4GroovyMockery();
+    private final ProjectInternal project = context.mock(AbstractProject.class, "[project]");
+    private final ProjectInternal otherProject = context.mock(AbstractProject.class, "[otherProject]");
+    private final ProjectInternal rootProject = context.mock(ProjectInternal.class);
     private final GradleInternal gradle = context.mock(GradleInternal.class);
-    private final TaskContainerInternal taskContainer = context.mock(TaskContainerInternal.class, "[projectTasks]");
-    private final TaskContainerInternal otherProjectTaskContainer = context.mock(TaskContainerInternal.class, "[otherProjectTasks]");
     private final TaskGraphExecuter taskExecuter = context.mock(TaskGraphExecuter.class);
-    private int counter;
+    private final TaskNameResolver resolver = context.mock(TaskNameResolver.class);
 
     @Before
     public void setUp() {
@@ -51,12 +55,12 @@ public class TaskNameResolvingBuildExecuterTest {
             will(returnValue(project));
             allowing(gradle).getTaskGraph();
             will(returnValue(taskExecuter));
-            allowing(project).getTasks();
-            will(returnValue(taskContainer));
             allowing(project).getAllprojects();
             will(returnValue(toSet(project, otherProject)));
-            allowing(otherProject).getTasks();
-            will(returnValue(otherProjectTaskContainer));
+            allowing(otherProject).getPath();
+            will(returnValue(":anotherProject"));
+            allowing(rootProject).getPath();
+            will(returnValue(":"));
         }});
     }
 
@@ -64,14 +68,15 @@ public class TaskNameResolvingBuildExecuterTest {
     public void selectsAllTasksWithTheProvidedNameInCurrentProjectAndSubprojects() {
         final Task task1 = task("name");
         final Task task2 = task("name");
+        final Task task3 = task("other");
 
         context.checking(new Expectations() {{
-            one(project).getTasksByName("name", true);
-            will(returnValue(toSet(task1, task2)));
+            one(resolver).selectAll("name", project);
+            will(returnValue(tasks(task1, task2, task3)));
             one(taskExecuter).addTasks(toSet(task1, task2));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("name"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("name"), resolver);
         executer.select(gradle);
         assertThat(executer.getDisplayName(), equalTo("primary task 'name'"));
     }
@@ -90,10 +95,11 @@ public class TaskNameResolvingBuildExecuterTest {
         assertMatches("a\\De(", "abc\\Def(", "a\\Df(");
     }
 
-    private void assertMatches(final String pattern, String matches, String... otherNames) {
+    private void assertMatches(final String pattern, final String matches, String... otherNames) {
+        final Set<Task> tasks = new HashSet<Task>();
         final Task task1 = task(matches);
+        tasks.add(task1);
         final Task task2 = task(matches);
-        final Set<Task> tasks = new HashSet<Task>();
         tasks.add(task2);
         for (String name : otherNames) {
             tasks.add(task(name));
@@ -102,16 +108,12 @@ public class TaskNameResolvingBuildExecuterTest {
         tasks.add(task("other"));
 
         context.checking(new Expectations() {{
-            one(project).getTasksByName(pattern, true);
-            will(returnValue(toSet()));
-            one(taskContainer).getAll();
-            will(returnValue(toSet(task1)));
-            one(otherProjectTaskContainer).getAll();
-            will(returnValue(tasks));
+            one(resolver).selectAll(pattern, project);
+            will(returnValue(tasks(tasks)));
             one(taskExecuter).addTasks(toSet(task1, task2));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList(pattern));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList(pattern), resolver);
         executer.select(gradle);
         assertThat(executer.getDisplayName(), equalTo(String.format("primary task '%s'", matches)));
     }
@@ -119,16 +121,17 @@ public class TaskNameResolvingBuildExecuterTest {
     @Test
     public void selectsTaskWithMatchingRelativePath() {
         final Task task1 = task("b");
+        final Task task2 = task("a");
 
         context.checking(new Expectations(){{
             one(project).getChildProjects();
             will(returnValue(toMap("a", otherProject)));
-            one(otherProjectTaskContainer).findByName("b");
-            will(returnValue(task1));
+            one(resolver).select("b", otherProject);
+            will(returnValue(tasks(task1, task2)));
             one(taskExecuter).addTasks(toSet(task1));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("a:b"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("a:b"), resolver);
         executer.select(gradle);
         assertThat(executer.getDisplayName(), equalTo("primary task 'a:b'"));
     }
@@ -136,16 +139,17 @@ public class TaskNameResolvingBuildExecuterTest {
     @Test
     public void selectsTaskWithMatchingTaskInRootProject() {
         final Task task1 = task("b");
+        final Task task2 = task("a");
 
         context.checking(new Expectations(){{
             one(project).getRootProject();
-            will(returnValue(otherProject));
-            one(otherProjectTaskContainer).findByName("b");
-            will(returnValue(task1));
+            will(returnValue(rootProject));
+            one(resolver).select("b", rootProject);
+            will(returnValue(tasks(task1, task2)));
             one(taskExecuter).addTasks(toSet(task1));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList(":b"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList(":b"), resolver);
         executer.select(gradle);
         assertThat(executer.getDisplayName(), equalTo("primary task ':b'"));
     }
@@ -153,18 +157,19 @@ public class TaskNameResolvingBuildExecuterTest {
     @Test
     public void selectsTaskWithMatchingAbsolutePath() {
         final Task task1 = task("b");
+        final Task task2 = task("a");
 
         context.checking(new Expectations(){{
             one(project).getRootProject();
-            will(returnValue(otherProject));
-            one(otherProject).getChildProjects();
+            will(returnValue(rootProject));
+            one(rootProject).getChildProjects();
             will(returnValue(toMap("a", otherProject)));
-            one(otherProjectTaskContainer).findByName("b");
-            will(returnValue(task1));
+            one(resolver).select("b", otherProject);
+            will(returnValue(tasks(task1, task2)));
             one(taskExecuter).addTasks(toSet(task1));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList(":a:b"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList(":a:b"), resolver);
         executer.select(gradle);
         assertThat(executer.getDisplayName(), equalTo("primary task ':a:b'"));
     }
@@ -176,19 +181,15 @@ public class TaskNameResolvingBuildExecuterTest {
 
         context.checking(new Expectations(){{
             one(project).getChildProjects();
-            will(returnValue(toMap("a", otherProject)));
-            one(otherProjectTaskContainer).findByName("soTa");
-            will(returnValue(null));
-            one(otherProjectTaskContainer).getAll();
-            will(returnValue(toSet(task1, task2)));
+            will(returnValue(toMap("anotherProject", otherProject)));
+            one(resolver).select("soTa", otherProject);
+            will(returnValue(tasks(task1, task2)));
             one(taskExecuter).addTasks(toSet(task1));
-            allowing(otherProject).getPath();
-            will(returnValue(":a"));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("a:soTa"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("anotherProject:soTa"), resolver);
         executer.select(gradle);
-        assertThat(executer.getDisplayName(), equalTo("primary task ':a:someTask'"));
+        assertThat(executer.getDisplayName(), equalTo("primary task ':anotherProject:someTask'"));
     }
 
     @Test
@@ -198,19 +199,15 @@ public class TaskNameResolvingBuildExecuterTest {
 
         context.checking(new Expectations(){{
             one(project).getChildProjects();
-            will(returnValue(toMap("someProject", otherProject)));
-            one(otherProjectTaskContainer).findByName("soTa");
-            will(returnValue(null));
-            one(otherProjectTaskContainer).getAll();
-            will(returnValue(toSet(task1, task2)));
+            will(returnValue(toMap("anotherProject", otherProject)));
+            one(resolver).select("soTa", otherProject);
+            will(returnValue(tasks(task1, task2)));
             one(taskExecuter).addTasks(toSet(task1));
-            allowing(otherProject).getPath();
-            will(returnValue(":someProject"));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("soPr:soTa"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("anPr:soTa"), resolver);
         executer.select(gradle);
-        assertThat(executer.getDisplayName(), equalTo("primary task ':someProject:someTask'"));
+        assertThat(executer.getDisplayName(), equalTo("primary task ':anotherProject:someTask'"));
     }
 
     @Test
@@ -219,15 +216,11 @@ public class TaskNameResolvingBuildExecuterTest {
         final Task task2 = task("someTasks");
 
         context.checking(new Expectations() {{
-            one(project).getTasksByName("soTa", true);
-            will(returnValue(toSet()));
-            one(taskContainer).getAll();
-            will(returnValue(toSet(task1)));
-            one(otherProjectTaskContainer).getAll();
-            will(returnValue(toSet(task2)));
+            one(resolver).selectAll("soTa", project);
+            will(returnValue(tasks(task1, task2)));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("soTa"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("soTa"), resolver);
         try {
             executer.select(gradle);
             fail();
@@ -244,15 +237,11 @@ public class TaskNameResolvingBuildExecuterTest {
         final Task task4 = task("other");
 
         context.checking(new Expectations() {{
-            one(project).getTasksByName("ssomeTask", true);
-            will(returnValue(toSet()));
-            one(taskContainer).getAll();
-            will(returnValue(toSet(task1, task2)));
-            one(otherProjectTaskContainer).getAll();
-            will(returnValue(toSet(task3, task4)));
+            one(resolver).selectAll("ssomeTask", project);
+            will(returnValue(tasks(task1, task2, task3, task4)));
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("ssomeTask"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("ssomeTask"), resolver);
         try {
             executer.select(gradle);
             fail();
@@ -267,13 +256,13 @@ public class TaskNameResolvingBuildExecuterTest {
         final Task task2 = task("name");
 
         context.checking(new Expectations() {{
-            one(project).getTasksByName("name", true);
-            will(returnValue(toSet(task1, task2)));
+            one(resolver).selectAll("name", project);
+            will(returnValue(tasks(task1, task2)));
             one(taskExecuter).addTasks(toSet(task1, task2));
             one(taskExecuter).execute();
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("name"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("name"), resolver);
         executer.select(gradle);
         executer.execute();
     }
@@ -286,10 +275,10 @@ public class TaskNameResolvingBuildExecuterTest {
         context.checking(new Expectations() {{
             one(project).getChildProjects();
             will(returnValue(toMap("child", otherProject)));
-            one(otherProjectTaskContainer).findByName("name1");
-            will(returnValue(task1));
-            one(project).getTasksByName("name2", true);
-            will(returnValue(toSet(task2)));
+            one(resolver).select("name1", otherProject);
+            will(returnValue(tasks(task1)));
+            one(resolver).selectAll("name2", project);
+            will(returnValue(tasks(task2)));
 
             Sequence sequence = context.sequence("tasks");
 
@@ -303,27 +292,50 @@ public class TaskNameResolvingBuildExecuterTest {
             inSequence(sequence);
         }});
 
-        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("child:name1", "name2"));
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("child:name1", "name2"), resolver);
         executer.select(gradle);
         assertThat(executer.getDisplayName(), equalTo("primary tasks 'child:name1', 'name2'"));
         executer.execute();
     }
 
     @Test
+    public void canConfigureSingleTaskUsingCommandLineOptions() {
+        final TaskWithBooleanProperty task1 = task("name1", TaskWithBooleanProperty.class);
+        final Task task2 = task("name2");
+
+        context.checking(new Expectations() {{
+            one(resolver).selectAll("name1", project);
+            will(returnValue(tasks(task1)));
+            one(resolver).selectAll("name2", project);
+            will(returnValue(tasks(task2)));
+
+            Sequence sequence = context.sequence("tasks");
+
+            one(task1).setSomeFlag(true);
+
+            one(taskExecuter).addTasks(toSet(task1));
+            inSequence(sequence);
+
+            one(taskExecuter).addTasks(toSet(task2));
+            inSequence(sequence);
+        }});
+
+        TaskNameResolvingBuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("name1", "--all", "name2"), resolver);
+        executer.select(gradle);
+        assertThat(executer.getDisplayName(), equalTo("primary tasks 'name1', 'name2'"));
+    }
+
+    @Test
     public void failsWhenUnknownTaskNameIsProvided() {
         final Task task1 = task("t1");
         final Task task2 = task("t2");
 
         context.checking(new Expectations() {{
-            one(project).getTasksByName("b3", true);
-            will(returnValue(toSet()));
-            one(taskContainer).getAll();
-            will(returnValue(toSet(task1, task2)));
-            one(otherProjectTaskContainer).getAll();
-            will(returnValue(toSet()));
+            one(resolver).selectAll("b3", project);
+            will(returnValue(tasks(task1, task2)));
         }});
 
-        BuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("b3"));
+        BuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("b3"), resolver);
         try {
             executer.select(gradle);
             fail();
@@ -339,7 +351,7 @@ public class TaskNameResolvingBuildExecuterTest {
             will(returnValue(GUtil.map("aa", otherProject, "ab", otherProject)));
         }});
 
-        BuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("a:b", "name2"));
+        BuildExecuter executer = new TaskNameResolvingBuildExecuter(toList("a:b", "name2"), resolver);
         try {
             executer.select(gradle);
             fail();
@@ -348,12 +360,33 @@ public class TaskNameResolvingBuildExecuterTest {
         }
     }
 
-    private Task task(final String name) {
-        final Task task = context.mock(Task.class, "task" + counter++ + "_" + name);
+    private Task task(String name) {
+        return task(name, Task.class);
+    }
+
+    private <T extends Task> T task(final String name, Class<T> taskType) {
+        final T task = context.mock(taskType);
         context.checking(new Expectations(){{
             allowing(task).getName();
             will(returnValue(name));
         }});
         return task;
     }
+
+    private Multimap<String, Task> tasks(Task... tasks) {
+        return tasks(Arrays.asList(tasks));
+    }
+
+    private Multimap<String, Task> tasks(Iterable<Task> tasks) {
+        Multimap<String, Task> map = LinkedHashMultimap.create();
+        for (Task task : tasks) {
+            map.put(task.getName(), task);
+        }
+        return map;
+    }
+
+    public abstract class TaskWithBooleanProperty implements Task {
+        @CommandLineOption(options = "all", description = "Some boolean flag")
+        public void setSomeFlag(boolean flag) { }
+    }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskReportBuildExecuterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskReportBuildExecuterTest.groovy
deleted file mode 100644
index e2de13d..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/execution/TaskReportBuildExecuterTest.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.execution
-
-import spock.lang.Specification
-import org.gradle.api.tasks.diagnostics.TaskReportTask
-
-class TaskReportBuildExecuterTest extends Specification {
-    private final TaskReportBuildExecuter executer = new TaskReportBuildExecuter("path", false)
-    
-    def constructsATaskReportTask() {
-        expect:
-        executer.taskType == TaskReportTask
-        executer.displayName == 'task list'
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildLoaderTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildLoaderTest.groovy
index e393dea..eb15c93 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildLoaderTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildLoaderTest.groovy
@@ -20,7 +20,6 @@ import org.gradle.StartParameter
 import org.gradle.api.GradleException
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.Project
-import org.gradle.api.artifacts.repositories.InternalRepository
 import org.gradle.api.initialization.ProjectDescriptor
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.project.DefaultProject
@@ -57,14 +56,12 @@ class BuildLoaderTest {
     ProjectInternal rootProject
     ProjectDescriptor childDescriptor
     ProjectInternal childProject
-    InternalRepository internalRepository
     GradleInternal build
     JUnit4GroovyMockery context = new JUnit4GroovyMockery()
     @Rule public TemporaryFolder tmpDir = new TemporaryFolder();
 
     @Before public void setUp()  {
         projectFactory = context.mock(IProjectFactory)
-        internalRepository = context.mock(InternalRepository)
         buildLoader = new BuildLoader(projectFactory)
         testDir = tmpDir.dir
         (rootProjectDir = new File(testDir, 'root')).mkdirs()
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy
index 934f034..6bcd471 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/BuildProgressLoggerTest.groovy
@@ -41,7 +41,7 @@ class BuildProgressLoggerTest extends Specification {
         logger.buildStarted(gradle)
 
         then:
-        1 * progressLoggerFactory.start() >> progressLogger
+        1 * progressLoggerFactory.start(BuildProgressLogger.name) >> progressLogger
         1 * progressLogger.progress('Loading')
 
         when:
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/CommandLineParserTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/CommandLineParserTest.groovy
new file mode 100644
index 0000000..00ba21b
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/CommandLineParserTest.groovy
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.initialization
+
+import spock.lang.Specification
+import org.gradle.CommandLineArgumentException
+
+class CommandLineParserTest extends Specification {
+    private final CommandLineParser parser = new CommandLineParser()
+
+    def parsesEmptyCommandLine() {
+        parser.option('a')
+        parser.option('long-value')
+
+        expect:
+        def result = parser.parse([])
+        !result.hasOption('a')
+        !result.hasOption('long-value')
+        result.extraArguments == []
+    }
+
+    def parsesShortOption() {
+        parser.option('a')
+        parser.option('b')
+
+        expect:
+        def result = parser.parse(['-a'])
+        result.hasOption('a')
+        !result.hasOption('b')
+    }
+
+    def canUseDoubleDashesForShortOptions() {
+        parser.option('a')
+
+        expect:
+        def result = parser.parse(['--a'])
+        result.hasOption('a')
+    }
+
+    def parsesShortOptionWithArgument() {
+        parser.option('a').hasArgument()
+
+        expect:
+        def result = parser.parse(['-a', 'arg'])
+        result.hasOption('a')
+        result.option('a').value == 'arg'
+        result.option('a').values == ['arg']
+    }
+
+    def parsesShortOptionWithAttachedArgument() {
+        parser.option('a').hasArgument()
+
+        expect:
+        def result = parser.parse(['-aarg'])
+        result.hasOption('a')
+        result.option('a').value == 'arg'
+        result.option('a').values == ['arg']
+    }
+
+    def attachedArgumentTakesPrecedenceOverCombinedOption() {
+        parser.option('a').hasArgument()
+        parser.option('b')
+
+        expect:
+        def result = parser.parse(['-ab'])
+        result.hasOption('a')
+        result.option('a').value == 'b'
+        !result.hasOption('b')
+    }
+
+    def parsesShortOptionWithEqualArgument() {
+        parser.option('a').hasArgument()
+
+        expect:
+        def result = parser.parse(['-a=arg'])
+        result.hasOption('a')
+        result.option('a').value == 'arg'
+        result.option('a').values == ['arg']
+    }
+
+    def parsesShortOptionWithEqualsCharacterInAttachedArgument() {
+        parser.option('a').hasArgument()
+
+        expect:
+        def result = parser.parse(['-avalue=arg'])
+        result.hasOption('a')
+        result.option('a').value == 'value=arg'
+        result.option('a').values == ['value=arg']
+    }
+
+    def parsesShortOptionWithDashCharacterInAttachedArgument() {
+        parser.option('a').hasArgument()
+
+        expect:
+        def result = parser.parse(['-avalue-arg'])
+        result.hasOption('a')
+        result.option('a').value == 'value-arg'
+        result.option('a').values == ['value-arg']
+    }
+
+    def parsesCombinedShortOptions() {
+        parser.option('a')
+        parser.option('b')
+
+        expect:
+        def result = parser.parse(['-ab'])
+        result.hasOption('a')
+        result.hasOption('b')
+    }
+
+    def parsesLongOption() {
+        parser.option('long-option-a')
+        parser.option('long-option-b')
+
+        expect:
+        def result = parser.parse(['--long-option-a'])
+        result.hasOption('long-option-a')
+        !result.hasOption('long-option-b')
+    }
+
+    def canUseSingleDashForLongOptions() {
+        parser.option('long')
+        parser.option('other').hasArgument()
+
+        expect:
+        def result = parser.parse(['-long', '-other', 'arg'])
+        result.hasOption('long')
+        result.hasOption('other')
+        result.option('other').value == 'arg'
+    }
+
+    def parsesLongOptionWithArgument() {
+        parser.option('long-option-a').hasArgument()
+        parser.option('long-option-b')
+
+        expect:
+        def result = parser.parse(['--long-option-a', 'arg'])
+        result.hasOption('long-option-a')
+        result.option('long-option-a').value == 'arg'
+        result.option('long-option-a').values == ['arg']
+    }
+
+    def parsesLongOptionWithEqualsArgument() {
+        parser.option('long-option-a').hasArgument()
+
+        expect:
+        def result = parser.parse(['--long-option-a=arg'])
+        result.hasOption('long-option-a')
+        result.option('long-option-a').value == 'arg'
+        result.option('long-option-a').values == ['arg']
+    }
+
+    def parsesMultipleOptions() {
+        parser.option('a').hasArgument()
+        parser.option('long-option')
+
+        expect:
+        def result = parser.parse(['--long-option', '-a', 'arg'])
+        result.hasOption('long-option')
+        result.hasOption('a')
+        result.option('a').value == 'arg'
+    }
+
+    def parsesOptionWithMultipleAliases() {
+        parser.option('a', 'b', 'long-option-a')
+
+        expect:
+        def result = parser.parse(['--long-option-a'])
+        result.hasOption('a')
+        result.hasOption('b')
+        result.hasOption('long-option-a')
+        result.option('a') == result.option('long-option-a')
+        result.option('a') == result.option('b')
+
+        result = parser.parse(['-a'])
+        result.hasOption('a')
+        result.hasOption('b')
+        result.hasOption('long-option-a')
+    }
+
+    def parsesCommandLineWhenOptionAppearsMultipleTimes() {
+        parser.option('a', 'b', 'long-option-a')
+
+        expect:
+        def result = parser.parse(['--long-option-a', '-a', '-a', '-b'])
+        result.hasOption('a')
+        result.hasOption('b')
+        result.hasOption('long-option-a')
+    }
+
+    def parsesOptionWithMultipleArguments() {
+        parser.option('a', 'long').hasArguments()
+
+        expect:
+        def result = parser.parse(['-a', 'arg1', '--long', 'arg2', '-aarg3', '--long=arg4'])
+        result.hasOption('a')
+        result.hasOption('long')
+        result.option('a').values == ['arg1', 'arg2', 'arg3', 'arg4']
+    }
+
+    def parsesCommandLineWithSubcommand() {
+        parser.option('a')
+
+        expect:
+        def result = parser.parse(['a'])
+        result.extraArguments == ['a']
+        !result.hasOption('a')
+
+        result = parser.parse(['a', 'b'])
+        result.extraArguments == ['a', 'b']
+        !result.hasOption('a')
+    }
+
+    def parsesCommandLineWithOptionsAndSubcommand() {
+        parser.option('a')
+
+        expect:
+        def result = parser.parse(['-a', 'a'])
+        result.extraArguments == ['a']
+        result.hasOption('a')
+
+        result = parser.parse(['a', '-a'])
+        result.extraArguments == ['a', '-a']
+        !result.hasOption('a')
+    }
+
+    def parsesCommandLineWithOptionsAndSubcommandWhenMixedOptionsAllowed() {
+        parser.option('a')
+        parser.allowMixedSubcommandsAndOptions()
+
+        expect:
+        def result = parser.parse(['-a', 'a'])
+        result.extraArguments == ['a']
+        result.hasOption('a')
+
+        result = parser.parse(['a', '-a'])
+        result.extraArguments == ['a']
+        result.hasOption('a')
+    }
+
+    def parsesCommandLineWithSubcommandThatHasOptions() {
+        expect:
+        def result = parser.parse(['a', '--option', 'b'])
+        result.extraArguments == ['a', '--option', 'b']
+    }
+    
+    def canTreatOptionAsSubcommand() {
+        parser.option('a').mapsToSubcommand('subcmd')
+
+        expect:
+        def result = parser.parse(['-a', '--option', 'b'])
+        result.extraArguments == ['subcmd', '--option', 'b']
+        result.hasOption('a')
+    }
+
+    def canCombineSubcommandShortOptionWithOtherShortOptions() {
+        parser.option('a').mapsToSubcommand('subcmd')
+        parser.option('b')
+
+        when:
+        def result = parser.parse(['-abc', '--option', 'b'])
+
+        then:
+        result.extraArguments == ['subcmd', '-b', '-c', '--option', 'b']
+        result.hasOption('a')
+        !result.hasOption('b')
+
+        when:
+        result = parser.parse(['-bac', '--option', 'b'])
+
+        then:
+        result.extraArguments == ['subcmd', '-c', '--option', 'b']
+        result.hasOption('a')
+        result.hasOption('b')
+
+        when:
+        parser.allowMixedSubcommandsAndOptions()
+        result = parser.parse(['-abc', '--option', 'b'])
+
+        then:
+        result.extraArguments == ['subcmd', '-c', '--option', 'b']
+        result.hasOption('a')
+        result.hasOption('b')
+
+        when:
+        result = parser.parse(['-bac', '--option', 'b'])
+
+        then:
+        result.extraArguments == ['subcmd', '-c', '--option', 'b']
+        result.hasOption('a')
+        result.hasOption('b')
+    }
+
+    def singleDashIsNotConsideredAnOption() {
+        expect:
+        def result = parser.parse(['-'])
+        result.extraArguments == ['-']
+    }
+
+    def doubleDashMarksEndOfOptions() {
+        parser.option('a')
+
+        expect:
+        def result = parser.parse(['--', '-a'])
+        result.extraArguments == ['-a']
+        !result.hasOption('a')
+    }
+
+    def valuesEmptyWhenOptionIsNotPresentInCommandLine() {
+        parser.option('a').hasArgument()
+
+        expect:
+        def result = parser.parse([])
+        result.option('a').values == []
+    }
+
+    def formatsUsageMessage() {
+        parser.option('a', 'long-option').hasDescription('this is option a')
+        parser.option('b')
+        parser.option('another-long-option').hasDescription('this is a long option')
+        parser.option('z', 'y', 'last-option', 'end-option').hasDescription('this is the last option')
+        parser.option('B')
+        def outstr = new ByteArrayOutputStream()
+
+        expect:
+        parser.printUsage(outstr)
+        outstr.toString().readLines() == [
+                '-a, --long-option                    this is option a',
+                '--another-long-option                this is a long option',
+                '-B',
+                '-b',
+                '-y, -z, --end-option, --last-option  this is the last option'
+        ]
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownShortOption() {
+        when:
+        parser.parse(['-a'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownShortOptionWithDoubleDashes() {
+        when:
+        parser.parse(['--a'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'--a\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownShortOptionWithEqualsArgument() {
+        when:
+        parser.parse(['-a=arg'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownShortOptionWithAttachedArgument() {
+        when:
+        parser.parse(['-aarg'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownLongOption() {
+        when:
+        parser.parse(['--unknown'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'--unknown\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownLongOptionWithSingleDashes() {
+        when:
+        parser.parse(['-unknown'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'-u\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsUnknownLongOptionWithEqualsArgument() {
+        when:
+        parser.parse(['--unknown=arg'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'--unknown\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsLongOptionWithAttachedArgument() {
+        parser.option("long").hasArgument()
+
+        when:
+        parser.parse(['--longvalue'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'--longvalue\'.'
+    }
+
+    def parseFailsWhenCommandLineContainsDashAndEquals() {
+        parser.option("long").hasArgument()
+
+        when:
+        parser.parse(['-='])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'-=\'.'
+    }
+
+    def getOptionFailsForUnknownOption() {
+        def result = parser.parse(['other'])
+
+        when:
+        result.option('unknown')
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == 'Option \'unknown\' not defined.'
+
+        when:
+        result.hasOption('unknown')
+
+        then:
+        e = thrown(IllegalArgumentException)
+        e.message == 'Option \'unknown\' not defined.'
+    }
+
+    def parseFailsWhenSingleValueOptionHasMultipleArguments() {
+        parser.option('a').hasArgument()
+
+        when:
+        parser.parse(['-a=arg1', '-a', 'arg2'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Multiple arguments were provided for command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenArgumentIsMissing() {
+        parser.option('a').hasArgument()
+
+        when:
+        parser.parse(['-a'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'No argument was provided for command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenArgumentIsMissingFromEqualsForm() {
+        parser.option('a').hasArgument()
+
+        when:
+        parser.parse(['-a='])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'An empty argument was provided for command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenEmptyArgumentIsProvided() {
+        parser.option('a').hasArgument()
+
+        when:
+        parser.parse(['-a', ''])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'An empty argument was provided for command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenArgumentIsMissingAndAnotherOptionFollows() {
+        parser.option('a').hasArgument()
+
+        when:
+        parser.parse(['-a', '-b'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'No argument was provided for command-line option \'-a\'.'
+    }
+
+    def parseFailsWhenArgumentIsMissingAndOptionsAreCombined() {
+        parser.option('a')
+        parser.option('b').hasArgument()
+
+        when:
+        parser.parse(['-ab'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'No argument was provided for command-line option \'-b\'.'
+    }
+
+    def parseFailsWhenAttachedArgumentIsProvidedForOptionWhichDoesNotTakeAnArgument() {
+        parser.option('a')
+
+        when:
+        parser.parse(['-aarg'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Unknown command-line option \'-r\'.'
+    }
+
+    def parseFailsWhenEqualsArgumentIsProvidedForOptionWhichDoesNotTakeAnArgument() {
+        parser.option('a')
+
+        when:
+        parser.parse(['-a=arg'])
+
+        then:
+        def e = thrown(CommandLineArgumentException)
+        e.message == 'Command-line option \'-a\' does not take an argument.'
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultCommandLine2StartParameterConverterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultCommandLine2StartParameterConverterTest.java
deleted file mode 100644
index 5477f54..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultCommandLine2StartParameterConverterTest.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.initialization;
-
-import org.gradle.CacheUsage;
-import org.gradle.CommandLineArgumentException;
-import org.gradle.GradleLauncher;
-import org.gradle.StartParameter;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.ProjectDependenciesBuildInstruction;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.execution.*;
-import org.gradle.groovy.scripts.UriScriptSource;
-import org.gradle.util.GUtil;
-import org.gradle.util.Matchers;
-import org.gradle.util.TemporaryFolder;
-import org.gradle.util.WrapUtil;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-
-import static org.gradle.util.WrapUtil.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-/**
- * @author Hans Dockter
- */
-public class DefaultCommandLine2StartParameterConverterTest {
-    private File expectedBuildFile;
-    private File expectedGradleUserHome = StartParameter.DEFAULT_GRADLE_USER_HOME;
-    private File expectedProjectDir;
-    private List<String> expectedTaskNames = toList();
-    private Set<String> expectedExcludedTasks = toSet();
-    private ProjectDependenciesBuildInstruction expectedProjectDependenciesBuildInstruction
-            = new ProjectDependenciesBuildInstruction(WrapUtil.<String>toList());
-    private Map<String, String> expectedSystemProperties = new HashMap<String, String>();
-    private Map<String, String> expectedProjectProperties = new HashMap<String, String>();
-    private List<File> expectedInitScripts = new ArrayList<File>();
-    private CacheUsage expectedCacheUsage = CacheUsage.ON;
-    private boolean expectedSearchUpwards = true;
-    private boolean expectedDryRun;
-    private boolean expectedShowHelp;
-    private boolean expectedShowVersion;
-    private StartParameter.ShowStacktrace expectedShowStackTrace = StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS;
-    private String expectedEmbeddedScript = "somescript";
-    private LogLevel expectedLogLevel = LogLevel.LIFECYCLE;
-    private StartParameter actualStartParameter;
-    @Rule
-    public TemporaryFolder testDir = new TemporaryFolder();
-
-    @Before
-    public void setUp() throws IOException {
-        expectedProjectDir = new File("").getCanonicalFile();
-    }
-
-    @After
-    public void tearDown() {
-        GradleLauncher.injectCustomFactory(null);
-    }
-
-    @Test
-    public void withoutAnyOptions() {
-        checkConversion();
-    }
-
-    private void checkConversion(String... args) {
-        checkConversion(false, false, args);
-    }
-
-    private void checkStartParameter(StartParameter startParameter, boolean emptyTasks) {
-        assertEquals(expectedBuildFile, startParameter.getBuildFile());
-        assertEquals(emptyTasks ? new ArrayList() : expectedTaskNames, startParameter.getTaskNames());
-        assertEquals(expectedProjectDependenciesBuildInstruction,
-                startParameter.getProjectDependenciesBuildInstruction());
-        assertEquals(expectedProjectDir.getAbsoluteFile(), startParameter.getCurrentDir().getAbsoluteFile());
-        assertEquals(expectedCacheUsage, startParameter.getCacheUsage());
-        assertEquals(expectedSearchUpwards, startParameter.isSearchUpwards());
-        assertEquals(expectedProjectProperties, startParameter.getProjectProperties());
-        assertEquals(expectedSystemProperties, startParameter.getSystemPropertiesArgs());
-        assertEquals(expectedGradleUserHome.getAbsoluteFile(), startParameter.getGradleUserHomeDir().getAbsoluteFile());
-        assertEquals(expectedGradleUserHome.getAbsoluteFile(), startParameter.getGradleUserHomeDir().getAbsoluteFile());
-        assertEquals(expectedLogLevel, startParameter.getLogLevel());
-        assertEquals(expectedDryRun, startParameter.isDryRun());
-        assertEquals(expectedShowHelp, startParameter.isShowHelp());
-        assertEquals(expectedShowVersion, startParameter.isShowVersion());
-        assertEquals(expectedShowStackTrace, startParameter.getShowStacktrace());
-        assertEquals(expectedExcludedTasks, startParameter.getExcludedTaskNames());
-        assertEquals(expectedInitScripts, startParameter.getInitScripts());
-    }
-
-    private void checkConversion(final boolean embedded, final boolean noTasks, String... args) {
-        actualStartParameter = new DefaultCommandLine2StartParameterConverter().convert(args);
-        // We check the params passed to the build factory
-        checkStartParameter(actualStartParameter, noTasks);
-        if (embedded) {
-            assertThat(actualStartParameter.getBuildScriptSource().getResource().getText(), equalTo(expectedEmbeddedScript));
-        } else {
-            assert !GUtil.isTrue(actualStartParameter.getBuildScriptSource());
-        }
-    }
-
-    @Test
-    public void withSpecifiedGradleUserHomeDirectory() {
-        expectedGradleUserHome = testDir.getDir();
-        checkConversion("-g", expectedGradleUserHome.getAbsoluteFile().toString());
-    }
-
-    @Test
-    public void withSpecifiedProjectDirectory() {
-        expectedProjectDir = testDir.getDir();
-        checkConversion("-p", expectedProjectDir.getAbsoluteFile().toString());
-    }
-
-    @Test
-    public void withSpecifiedBuildFileName() throws IOException {
-        expectedBuildFile = new File("somename").getCanonicalFile();
-        checkConversion("-b", "somename");
-    }
-
-    @Test
-    public void withSpecifiedSettingsFileName() throws IOException {
-        checkConversion("-c", "somesettings");
-
-        assertThat(actualStartParameter.getSettingsScriptSource(), instanceOf(UriScriptSource.class));
-        assertThat(actualStartParameter.getSettingsScriptSource().getResource().getFile(), equalTo(new File(
-                "somesettings").getCanonicalFile()));
-    }
-
-    @Test
-    public void withSystemProperties() {
-        final String prop1 = "gradle.prop1";
-        final String valueProp1 = "value1";
-        final String prop2 = "gradle.prop2";
-        final String valueProp2 = "value2";
-        expectedSystemProperties = toMap(prop1, valueProp1);
-        expectedSystemProperties.put(prop2, valueProp2);
-        checkConversion("-D", prop1 + "=" + valueProp1, "-D", prop2 + "=" + valueProp2);
-    }
-
-    @Test
-    public void withStartProperties() {
-        final String prop1 = "prop1";
-        final String valueProp1 = "value1";
-        final String prop2 = "prop2";
-        final String valueProp2 = "value2";
-        expectedProjectProperties = toMap(prop1, valueProp1);
-        expectedProjectProperties.put(prop2, valueProp2);
-        checkConversion("-P", prop1 + "=" + valueProp1, "-P", prop2 + "=" + valueProp2);
-    }
-
-    @Test
-    public void withTaskNames() {
-        expectedTaskNames = toList("a", "b");
-        checkConversion("a", "b");
-    }
-
-    @Test
-    public void withRebuildCacheFlagSet() {
-        expectedCacheUsage = CacheUsage.REBUILD;
-        checkConversion("-C", "rebuild");
-    }
-
-    @Test
-    public void withCacheOnFlagSet() {
-        checkConversion("-C", "on");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withUnknownCacheFlags() {
-        checkConversion("-C", "unknown");
-    }
-
-    @Test
-    public void withSearchUpwardsFlagSet() {
-        expectedSearchUpwards = false;
-        checkConversion("-u");
-    }
-
-    @Test
-    public void withShowFullStacktrace() {
-        expectedShowStackTrace = StartParameter.ShowStacktrace.ALWAYS_FULL;
-        checkConversion("-S");
-    }
-
-    @Test
-    public void withShowStacktrace() {
-        expectedShowStackTrace = StartParameter.ShowStacktrace.ALWAYS;
-        checkConversion("-s");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withShowStacktraceAndShowFullStacktraceShouldThrowCommandLineArgumentEx() {
-        checkConversion("-sf");
-    }
-
-    @Test
-    public void withDryRunFlagSet() {
-        expectedDryRun = true;
-        checkConversion("-m");
-    }
-
-    @Test
-    public void withExcludeTask() {
-        expectedExcludedTasks.add("excluded");
-        checkConversion("-x", "excluded");
-        expectedExcludedTasks.add("excluded2");
-        checkConversion("-x", "excluded", "-x", "excluded2");
-    }
-
-    @Test
-    public void withShowHelp() {
-        expectedShowHelp = true;
-        checkConversion("-h");
-    }
-
-    @Test
-    public void withShowVersion() {
-        expectedShowVersion = true;
-        checkConversion("-v");
-    }
-
-    @Test
-    public void withEmbeddedScript() {
-        expectedSearchUpwards = false;
-        checkConversion(true, false, "-e", expectedEmbeddedScript);
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withEmbeddedScriptAndConflictingNoSearchUpwardsOption() {
-        checkConversion("-e", "someScript", "-u", "clean");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withEmbeddedScriptAndConflictingSpecifyBuildFileOption() {
-        checkConversion("-e", "someScript", "-bsomeFile", "clean");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withEmbeddedScriptAndConflictingSpecifySettingsFileOption() {
-        checkConversion("-e", "someScript", "-csomeFile", "clean");
-    }
-
-    @Test
-    public void withConflictingLoggingOptionsDQ() {
-        List<String> illegalOptions = toList("dq", "di", "qd", "qi", "iq", "id");
-        for (String illegalOption : illegalOptions) {
-            try {
-                checkConversion("-" + illegalOption, "clean");
-            } catch (InvalidUserDataException e) {
-                continue;
-            }
-            fail("Expected InvalidUserDataException");
-        }
-    }
-
-    @Test
-    public void withQuietLoggingOptions() {
-        expectedLogLevel = LogLevel.QUIET;
-        checkConversion("-q");
-    }
-
-    @Test
-    public void withNoProjectDependencyRebuild() {
-        expectedProjectDependenciesBuildInstruction = new ProjectDependenciesBuildInstruction(null);
-        checkConversion("-a");
-    }
-
-    @Test
-    public void withProjectDependencyTaskNames() {
-        expectedProjectDependenciesBuildInstruction = new ProjectDependenciesBuildInstruction(WrapUtil.toList("task1",
-                "task2"));
-        checkConversion("-Atask1", "-A task2");
-    }
-
-    @Test
-    public void withInfoLoggingOptions() {
-        expectedLogLevel = LogLevel.INFO;
-        checkConversion("-i");
-    }
-
-    @Test
-    public void withDebugLoggingOptions() {
-        expectedLogLevel = LogLevel.DEBUG;
-        checkConversion("-d");
-    }
-
-    @Test
-    public void withShowTasks() {
-        checkConversion(false, true, "-t");
-        BuildExecuter expectedExecuter = new TaskReportBuildExecuter(null, false);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test
-    public void withShowAllTasks() {
-        checkConversion(false, true, "-t", "--all");
-        BuildExecuter expectedExecuter = new TaskReportBuildExecuter(null, true);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test
-    public void withShowTasksAndEmbeddedScript() {
-        expectedSearchUpwards = false;
-        checkConversion(true, true, "-e", expectedEmbeddedScript, "-t");
-    }
-
-    @Test
-    public void withShowTasksAndPath() {
-        String somePath = ":SomeProject";
-        checkConversion(false, true, "-t" + somePath);
-        BuildExecuter expectedExecuter = new TaskReportBuildExecuter(somePath, false);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test
-    public void withShowProperties() {
-        checkConversion(false, true, "-r");
-        BuildExecuter expectedExecuter = new PropertyReportBuildExecuter(null);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test
-    public void withShowPropertiesAndPath() {
-        String somePath = ":SomeProject";
-        checkConversion(false, true, "-r" + somePath);
-        BuildExecuter expectedExecuter = new PropertyReportBuildExecuter(somePath);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test
-    public void withShowDependencies() {
-        checkConversion(false, true, "-n");
-        BuildExecuter expectedExecuter = new DependencyReportBuildExecuter(null);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test
-    public void withShowDependenciesAndPath() {
-        String somePath = ":SomeProject";
-        checkConversion(false, true, "-n" + somePath);
-        BuildExecuter expectedExecuter = new DependencyReportBuildExecuter(somePath);
-        assertThat(actualStartParameter.getBuildExecuter(), Matchers.reflectionEquals(expectedExecuter));
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withShowTasksPropertiesAndDependencies() {
-        checkConversion("-r", "-t");
-        checkConversion("-r", "-n");
-        checkConversion("-r", "-n", "-t");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withLowerPParameterWithoutArgument() {
-        checkConversion("-p");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withAParameterWithoutArgument() {
-        checkConversion("-A");
-    }
-
-    @Test(expected = CommandLineArgumentException.class)
-    public void withUpperAAndLowerAParameter() {
-        checkConversion("-a -Atask1");
-    }
-
-    @Test
-    public void withInitScripts() {
-        File script1 = new File("init1.gradle");
-        expectedInitScripts.add(script1);
-        checkConversion("-Iinit1.gradle");
-
-        File script2 = new File("init2.gradle");
-        expectedInitScripts.add(script2);
-        checkConversion("-Iinit1.gradle", "-Iinit2.gradle");
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
new file mode 100644
index 0000000..176da87
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultCommandLineConverterTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.initialization;
+
+import org.gradle.CacheUsage;
+import org.gradle.CommandLineArgumentException;
+import org.gradle.StartParameter;
+import org.gradle.api.internal.artifacts.ProjectDependenciesBuildInstruction;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.groovy.scripts.UriScriptSource;
+import org.gradle.util.GUtil;
+import org.gradle.util.TemporaryFolder;
+import org.gradle.util.WrapUtil;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+import static org.gradle.util.WrapUtil.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Hans Dockter
+ */
+public class DefaultCommandLineConverterTest {
+    private File expectedBuildFile;
+    private File expectedGradleUserHome = StartParameter.DEFAULT_GRADLE_USER_HOME;
+    private File expectedProjectDir;
+    private List<String> expectedTaskNames = toList();
+    private Set<String> expectedExcludedTasks = toSet();
+    private ProjectDependenciesBuildInstruction expectedProjectDependenciesBuildInstruction
+            = new ProjectDependenciesBuildInstruction(WrapUtil.<String>toList());
+    private Map<String, String> expectedSystemProperties = new HashMap<String, String>();
+    private Map<String, String> expectedProjectProperties = new HashMap<String, String>();
+    private List<File> expectedInitScripts = new ArrayList<File>();
+    private CacheUsage expectedCacheUsage = CacheUsage.ON;
+    private boolean expectedSearchUpwards = true;
+    private boolean expectedDryRun;
+    private StartParameter.ShowStacktrace expectedShowStackTrace = StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS;
+    private String expectedEmbeddedScript = "somescript";
+    private LogLevel expectedLogLevel = LogLevel.LIFECYCLE;
+    private boolean expectedColorOutput = true;
+    private StartParameter actualStartParameter;
+    private boolean expectedProfile;
+
+    @Rule
+    public TemporaryFolder testDir = new TemporaryFolder();
+    private final DefaultCommandLineConverter commandLineConverter = new DefaultCommandLineConverter();
+
+    @Before
+    public void setUp() throws IOException {
+        expectedProjectDir = new File("").getCanonicalFile();
+    }
+
+    @Test
+    public void withoutAnyOptions() {
+        checkConversion();
+    }
+
+    private void checkConversion(String... args) {
+        checkConversion(false, args);
+    }
+
+    private void checkStartParameter(StartParameter startParameter) {
+        assertEquals(expectedBuildFile, startParameter.getBuildFile());
+        assertEquals(expectedTaskNames, startParameter.getTaskNames());
+        assertEquals(expectedProjectDependenciesBuildInstruction,
+                startParameter.getProjectDependenciesBuildInstruction());
+        assertEquals(expectedProjectDir.getAbsoluteFile(), startParameter.getCurrentDir().getAbsoluteFile());
+        assertEquals(expectedCacheUsage, startParameter.getCacheUsage());
+        assertEquals(expectedSearchUpwards, startParameter.isSearchUpwards());
+        assertEquals(expectedProjectProperties, startParameter.getProjectProperties());
+        assertEquals(expectedSystemProperties, startParameter.getSystemPropertiesArgs());
+        assertEquals(expectedGradleUserHome.getAbsoluteFile(), startParameter.getGradleUserHomeDir().getAbsoluteFile());
+        assertEquals(expectedGradleUserHome.getAbsoluteFile(), startParameter.getGradleUserHomeDir().getAbsoluteFile());
+        assertEquals(expectedLogLevel, startParameter.getLogLevel());
+        assertEquals(expectedColorOutput, startParameter.isColorOutput());
+        assertEquals(expectedDryRun, startParameter.isDryRun());
+        assertEquals(expectedShowStackTrace, startParameter.getShowStacktrace());
+        assertEquals(expectedExcludedTasks, startParameter.getExcludedTaskNames());
+        assertEquals(expectedInitScripts, startParameter.getInitScripts());
+        assertEquals(expectedProfile, startParameter.isProfile());
+    }
+
+    private void checkConversion(final boolean embedded, String... args) {
+        actualStartParameter = commandLineConverter.convert(Arrays.asList(args));
+        // We check the params passed to the build factory
+        checkStartParameter(actualStartParameter);
+        if (embedded) {
+            assertThat(actualStartParameter.getBuildScriptSource().getResource().getText(), equalTo(expectedEmbeddedScript));
+        } else {
+            assert !GUtil.isTrue(actualStartParameter.getBuildScriptSource());
+        }
+    }
+
+    @Test
+    public void withSpecifiedGradleUserHomeDirectory() {
+        expectedGradleUserHome = testDir.getDir();
+        checkConversion("-g", expectedGradleUserHome.getAbsoluteFile().toString());
+    }
+
+    @Test
+    public void withSpecifiedProjectDirectory() {
+        expectedProjectDir = testDir.getDir();
+        checkConversion("-p", expectedProjectDir.getAbsoluteFile().toString());
+    }
+
+    @Test
+    public void withSpecifiedBuildFileName() throws IOException {
+        expectedBuildFile = new File("somename").getCanonicalFile();
+        checkConversion("-b", "somename");
+    }
+
+    @Test
+    public void withSpecifiedSettingsFileName() throws IOException {
+        checkConversion("-c", "somesettings");
+
+        assertThat(actualStartParameter.getSettingsScriptSource(), instanceOf(UriScriptSource.class));
+        assertThat(actualStartParameter.getSettingsScriptSource().getResource().getFile(), equalTo(new File(
+                "somesettings").getCanonicalFile()));
+    }
+
+    @Test
+    public void withSystemProperties() {
+        final String prop1 = "gradle.prop1";
+        final String valueProp1 = "value1";
+        final String prop2 = "gradle.prop2";
+        final String valueProp2 = "value2";
+        expectedSystemProperties = toMap(prop1, valueProp1);
+        expectedSystemProperties.put(prop2, valueProp2);
+        checkConversion("-D", prop1 + "=" + valueProp1, "-D", prop2 + "=" + valueProp2);
+    }
+
+    @Test
+    public void withStartProperties() {
+        final String prop1 = "prop1";
+        final String valueProp1 = "value1";
+        final String prop2 = "prop2";
+        final String valueProp2 = "value2";
+        expectedProjectProperties = toMap(prop1, valueProp1);
+        expectedProjectProperties.put(prop2, valueProp2);
+        checkConversion("-P", prop1 + "=" + valueProp1, "-P", prop2 + "=" + valueProp2);
+    }
+
+    @Test
+    public void withTaskNames() {
+        expectedTaskNames = toList("a", "b");
+        checkConversion("a", "b");
+    }
+
+    @Test
+    public void withRebuildCacheFlagSet() {
+        expectedCacheUsage = CacheUsage.REBUILD;
+        checkConversion("-C", "rebuild");
+    }
+
+    @Test
+    public void withCacheOnFlagSet() {
+        checkConversion("-C", "on");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withUnknownCacheFlags() {
+        checkConversion("-C", "unknown");
+    }
+
+    @Test
+    public void withSearchUpwardsFlagSet() {
+        expectedSearchUpwards = false;
+        checkConversion("-u");
+    }
+
+    @Test
+    public void withShowFullStacktrace() {
+        expectedShowStackTrace = StartParameter.ShowStacktrace.ALWAYS_FULL;
+        checkConversion("-S");
+    }
+
+    @Test
+    public void withShowStacktrace() {
+        expectedShowStackTrace = StartParameter.ShowStacktrace.ALWAYS;
+        checkConversion("-s");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withShowStacktraceAndShowFullStacktraceShouldThrowCommandLineArgumentEx() {
+        checkConversion("-sf");
+    }
+
+    @Test
+    public void withDryRunFlagSet() {
+        expectedDryRun = true;
+        checkConversion("-m");
+    }
+
+    @Test
+    public void withExcludeTask() {
+        expectedExcludedTasks.add("excluded");
+        checkConversion("-x", "excluded");
+        expectedExcludedTasks.add("excluded2");
+        checkConversion("-x", "excluded", "-x", "excluded2");
+    }
+
+    @Test
+    public void withEmbeddedScript() {
+        expectedSearchUpwards = false;
+        checkConversion(true, "-e", expectedEmbeddedScript);
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withEmbeddedScriptAndConflictingNoSearchUpwardsOption() {
+        checkConversion("-e", "someScript", "-u", "clean");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withEmbeddedScriptAndConflictingSpecifyBuildFileOption() {
+        checkConversion("-e", "someScript", "-bsomeFile", "clean");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withEmbeddedScriptAndConflictingSpecifySettingsFileOption() {
+        checkConversion("-e", "someScript", "-csomeFile", "clean");
+    }
+
+    @Test
+    public void withNoProjectDependencyRebuild() {
+        expectedProjectDependenciesBuildInstruction = new ProjectDependenciesBuildInstruction(null);
+        checkConversion("-a");
+    }
+
+    @Test
+    public void withProjectDependencyTaskNames() {
+        expectedProjectDependenciesBuildInstruction = new ProjectDependenciesBuildInstruction(WrapUtil.toList("task1",
+                "task2"));
+        checkConversion("-Atask1", "-A", "task2");
+    }
+
+    @Test
+    public void withQuietLoggingOptions() {
+        expectedLogLevel = LogLevel.QUIET;
+        checkConversion("-q");
+    }
+
+    @Test
+    public void withInfoLoggingOptions() {
+        expectedLogLevel = LogLevel.INFO;
+        checkConversion("-i");
+    }
+
+    @Test
+    public void withDebugLoggingOptions() {
+        expectedLogLevel = LogLevel.DEBUG;
+        checkConversion("-d");
+    }
+
+    @Test
+    public void withNoColor() {
+        expectedColorOutput = false;
+        checkConversion("--no-color");
+    }
+
+    @Test
+    public void withShowTasks() {
+        expectedTaskNames = toList("tasks");
+        checkConversion(false, "-t");
+    }
+
+    @Test
+    public void withShowAllTasks() {
+        expectedTaskNames = toList("tasks", "--all");
+        checkConversion(false, "-t", "--all");
+    }
+
+    @Test
+    public void withShowTasksAndEmbeddedScript() {
+        expectedSearchUpwards = false;
+        expectedTaskNames = toList("tasks");
+        checkConversion(true, "-e", expectedEmbeddedScript, "-t");
+    }
+
+    @Test
+    public void withShowProperties() {
+        expectedTaskNames = toList("properties");
+        checkConversion(false, "-r");
+    }
+
+    @Test
+    public void withShowDependencies() {
+        expectedTaskNames = toList("dependencies");
+        checkConversion(false, "-n");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withLowerPParameterWithoutArgument() {
+        checkConversion("-p");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withAParameterWithoutArgument() {
+        checkConversion("-A");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withUpperAAndLowerAParameter() {
+        checkConversion("-a", "-Atask1");
+    }
+
+    @Test
+    public void withInitScripts() {
+        File script1 = new File("init1.gradle");
+        expectedInitScripts.add(script1);
+        checkConversion("-Iinit1.gradle");
+
+        File script2 = new File("init2.gradle");
+        expectedInitScripts.add(script2);
+        checkConversion("-Iinit1.gradle", "-Iinit2.gradle");
+    }
+
+    @Test
+    public void withProfile() {
+        expectedProfile = true;
+        checkConversion("--profile");
+    }
+
+    @Test(expected = CommandLineArgumentException.class)
+    public void withUnknownOption() {
+        checkConversion("--unknown");
+    }
+
+    @Test
+    public void withTaskAndTaskOption() {
+        expectedTaskNames = toList("someTask", "--some-task-option");
+        checkConversion("someTask", "--some-task-option");
+    }
+
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.java
index ea4c7c5..a634303 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherFactoryTest.java
@@ -15,61 +15,72 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.GradleLauncher;
 import org.gradle.StartParameter;
-import org.gradle.logging.LoggingConfigurer;
-import org.gradle.util.WrapUtil;
-import org.hamcrest.Matchers;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.gradle.util.WrapUtil.toList;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 
 /**
  * @author Hans Dockter
  */
 public class DefaultGradleLauncherFactoryTest {
     private JUnit4Mockery context = new JUnit4Mockery();
-    private final LoggingConfigurer loggingConfigurer = context.mock(LoggingConfigurer.class);
-    private final CommandLine2StartParameterConverter parameterConverter = context.mock(CommandLine2StartParameterConverter.class);
+    private final CommandLineConverter<StartParameter> parameterConverter = context.mock(CommandLineConverter.class);
     private final DefaultGradleLauncherFactory factory = new DefaultGradleLauncherFactory();
 
     @Before
     public void setUp() {
-        factory.setCommandLine2StartParameterConverter(parameterConverter);
+        factory.setCommandLineConverter(parameterConverter);
+    }
+
+    @After
+    public void tearDown() {
+        GradleLauncher.injectCustomFactory(null);
     }
 
     @Test
-    public void newInstanceWithStartParameter() {
+    public void registersSelfWithGradleLauncher() {
         final StartParameter startParameter = new StartParameter();
         context.checking(new Expectations() {{
-            one(loggingConfigurer).configure(startParameter.getLogLevel());
+            allowing(parameterConverter).convert(toList("a"));
+            will(returnValue(startParameter));
         }});
+
+        assertThat(GradleLauncher.createStartParameter("a"), sameInstance(startParameter));
+    }
+    
+    @Test
+    public void newInstanceWithStartParameter() {
+        final StartParameter startParameter = new StartParameter();
         assertNotNull(factory.newInstance(startParameter));
     }
 
     @Test
     public void newInstanceWithCommandLineArgs() {
         final StartParameter startParameter = new StartParameter();
-        final String[] commandLineArgs = WrapUtil.toArray("A", "B");
         context.checking(new Expectations() {{
-            one(loggingConfigurer).configure(startParameter.getLogLevel());
-            allowing(parameterConverter).convert(commandLineArgs); will(returnValue(startParameter));
+            allowing(parameterConverter).convert(toList("A", "B"));
+            will(returnValue(startParameter));
         }});
-        assertNotNull(factory.newInstance(commandLineArgs));
+        assertNotNull(factory.newInstance("A", "B"));
     }
 
     @Test
     public void createStartParameter() {
         final StartParameter startParameter = new StartParameter();
-        final String[] commandLineArgs = WrapUtil.toArray("A", "B");
         context.checking(new Expectations() {{
-            one(loggingConfigurer).configure(startParameter.getLogLevel());
-            allowing(parameterConverter).convert(commandLineArgs); will(returnValue(startParameter));
+            allowing(parameterConverter).convert(toList("A", "B"));
+            will(returnValue(startParameter));
         }});
 
-        assertThat(factory.createStartParameter(commandLineArgs),
-            Matchers.sameInstance(startParameter));
+        assertThat(factory.createStartParameter("A", "B"), sameInstance(startParameter));
     }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
index 1569468..f4b70a0 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultGradleLauncherTest.java
@@ -28,7 +28,6 @@ import org.gradle.api.internal.SettingsInternal;
 import org.gradle.api.internal.project.DefaultProject;
 import org.gradle.configuration.BuildConfigurer;
 import org.gradle.execution.TaskGraphExecuter;
-import org.gradle.logging.LoggingConfigurer;
 import org.gradle.logging.LoggingManagerInternal;
 import org.gradle.util.HelperUtil;
 import org.gradle.util.TemporaryFolder;
@@ -51,7 +50,7 @@ import java.util.Map;
 
 import static org.gradle.util.WrapUtil.*;
 import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
 
 /**
  * @author Hans Dockter
@@ -82,8 +81,6 @@ public class DefaultGradleLauncherTest {
 
     private JUnit4Mockery context = new JUnit4Mockery();
 
-    private LoggingConfigurer loggingConfigurerMock = context.mock(LoggingConfigurer.class);
-
     private ExceptionAnalyser exceptionAnalyserMock = context.mock(ExceptionAnalyser.class);
 
     private LoggingManagerInternal loggingManagerMock = context.mock(LoggingManagerInternal.class);
@@ -201,7 +198,7 @@ public class DefaultGradleLauncherTest {
         expectBuildListenerCallbacks();
         context.checking(new Expectations() {{
             one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, testGradleProperties);
-            one(buildConfigurerMock).process(expectedRootProject);
+            one(buildConfigurerMock).configure(gradleMock);
         }});
         BuildResult buildResult = gradleLauncher.getBuildAnalysis();
         assertThat(buildResult.getFailure(), nullValue());
@@ -236,7 +233,7 @@ public class DefaultGradleLauncherTest {
         expectBuildListenerCallbacks();
         context.checking(new Expectations() {{
             one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, testGradleProperties);
-            one(buildConfigurerMock).process(expectedRootProject);
+            one(buildConfigurerMock).configure(gradleMock);
         }});
 
         gradleLauncher.getBuildAnalysis();
@@ -331,7 +328,7 @@ public class DefaultGradleLauncherTest {
         context.checking(new Expectations() {
             {
                 one(buildLoaderMock).load(expectedRootProjectDescriptor, gradleMock, testGradleProperties);
-                one(buildConfigurerMock).process(expectedRootProject);
+                one(buildConfigurerMock).configure(gradleMock);
                 one(taskExecuterMock).addTasks(expectedTasks.get(0));
                 one(taskExecuterMock).addTasks(expectedTasks.get(1));
             }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java
index ae46103..c5f6a32 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorRegistryTest.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.util.Path;
 import org.junit.Test;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.*;
@@ -44,7 +45,7 @@ public class DefaultProjectDescriptorRegistryTest {
         DefaultProjectDescriptor project = new DefaultProjectDescriptor(null, "name", TEST_DIR, registry);
         registry.addProject(project);
 
-        registry.changeDescriptorPath(":", ":newPath");
+        registry.changeDescriptorPath(Path.path(":"), Path.path(":newPath"));
         assertThat(registry.getProject(":"), nullValue());
         assertThat(registry.getProject(":newPath"), sameInstance(project));
     }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java
index 890474d..54c121d 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/initialization/DefaultProjectDescriptorTest.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.initialization;
 
+import org.gradle.util.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -72,8 +73,7 @@ public class DefaultProjectDescriptorTest {
         final IProjectDescriptorRegistry projectDescriptorRegistryMock = context.mock(IProjectDescriptorRegistry.class);
         projectDescriptor.setProjectDescriptorRegistry(projectDescriptorRegistryMock);
         context.checking(new Expectations() {{
-            one(projectDescriptorRegistryMock).changeDescriptorPath(projectDescriptor.getPath(),
-                    Project.PATH_SEPARATOR + newName);
+            one(projectDescriptorRegistryMock).changeDescriptorPath(Path.path(TEST_NAME), Path.path(Project.PATH_SEPARATOR + newName));
         }});
         projectDescriptor.setName(newName);
         assertEquals(newName, projectDescriptor.getName());
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
index 1dc45fe..5ff2c10 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/invocation/DefaultGradleTest.java
@@ -24,6 +24,7 @@ import org.gradle.api.internal.GradleDistributionLocator;
 import org.gradle.api.internal.initialization.ScriptClassLoaderProvider;
 import org.gradle.api.internal.plugins.PluginRegistry;
 import org.gradle.api.internal.project.IProjectRegistry;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.ServiceRegistryFactory;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.execution.TaskGraphExecuter;
@@ -47,7 +48,7 @@ import static org.junit.Assert.*;
 
 @RunWith(JUnit4.class)
 public class DefaultGradleTest {
-    private final JUnit4Mockery context = new JUnit4Mockery(){{
+    private final JUnit4Mockery context = new JUnit4Mockery() {{
         setImposteriser(ClassImposteriser.INSTANCE);
     }};
     private final StartParameter parameter = new StartParameter();
@@ -65,7 +66,7 @@ public class DefaultGradleTest {
 
     @Before
     public void setUp() {
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(serviceRegistryFactoryMock).createFor(with(any(DefaultGradle.class)));
             will(returnValue(gradleServiceRegistryMock));
             allowing(gradleServiceRegistryMock).get(ScriptHandler.class);
@@ -91,11 +92,11 @@ public class DefaultGradleTest {
     @Test
     public void defaultValues() {
         assertThat(gradle.getParent(), sameInstance(parent));
-        assertThat(gradle.getServiceRegistryFactory(), sameInstance(gradleServiceRegistryMock));
+        assertThat(gradle.getServices(), sameInstance(gradleServiceRegistryMock));
         assertThat(gradle.getProjectRegistry(), sameInstance(projectRegistry));
         assertThat(gradle.getTaskGraph(), sameInstance(taskExecuter));
     }
-    
+
     @Test
     public void usesGradleVersion() {
         assertThat(gradle.getGradleVersion(), equalTo(new GradleVersion().getVersion()));
@@ -138,7 +139,7 @@ public class DefaultGradleTest {
     @Test
     public void broadcastsBeforeProjectEvaluateEventsToClosures() {
         final Closure closure = HelperUtil.TEST_CLOSURE;
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(listenerManager).addListener(ProjectEvaluationListener.class, "beforeEvaluate", closure);
         }});
 
@@ -148,7 +149,7 @@ public class DefaultGradleTest {
     @Test
     public void broadcastsAfterProjectEvaluateEventsToClosures() {
         final Closure closure = HelperUtil.TEST_CLOSURE;
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(listenerManager).addListener(ProjectEvaluationListener.class, "afterEvaluate", closure);
         }});
 
@@ -158,9 +159,22 @@ public class DefaultGradleTest {
     @Test
     public void usesSpecifiedLogger() {
         final Object logger = new Object();
-        context.checking(new Expectations(){{
+        context.checking(new Expectations() {{
             one(listenerManager).useLogger(logger);
         }});
         gradle.useLogger(logger);
     }
+
+    @Test
+    public void hasToString() {
+        assertThat(gradle.toString(), equalTo("build"));
+
+        final ProjectInternal project = context.mock(ProjectInternal.class);
+        context.checking(new Expectations(){{
+            allowing(project).getName();
+            will(returnValue("rootProject"));
+        }});
+        gradle.setRootProject(project);
+        assertThat(gradle.toString(), equalTo("build 'rootProject'"));
+    }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/AnsiConsoleTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/AnsiConsoleTest.groovy
deleted file mode 100644
index a20f7ce..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/AnsiConsoleTest.groovy
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging
-
-import org.fusesource.jansi.Ansi
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Test
-import org.junit.runner.RunWith
-
- at RunWith(JMock.class)
-class AnsiConsoleTest {
-    private static final String EOL = System.getProperty('line.separator')
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final Ansi ansi = context.mock(Ansi.class)
-    private final Appendable target = {} as Appendable
-    private final Flushable flushable = {} as Flushable
-    private final AnsiConsole console = new AnsiConsole(target, flushable) {
-        def Ansi createAnsi() {
-            return ansi
-        }
-    }
-
-    @Test
-    public void appendsTextToMainArea() {
-        context.checking {
-            one(ansi).a('message')
-        }
-
-        console.mainArea.append('message')
-
-        context.checking {
-            one(ansi).a('message2')
-            one(ansi).a(EOL)
-            one(ansi).a('message3')
-        }
-
-        console.mainArea.append("message2${EOL}message3")
-    }
-
-    @Test
-    public void displaysStatusBarWithNonEmptyText() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text')
-        }
-
-        statusBar.text = 'text'
-    }
-
-    @Test
-    public void displaysStatusBarWhenTextInMainArea() {
-        context.checking {
-            one(ansi).a('message')
-            one(ansi).a(EOL)
-        }
-
-        console.mainArea.append("message${EOL}")
-
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text')
-        }
-
-        statusBar.text = 'text'
-    }
-
-    @Test
-    public void redrawsStatusBarWhenTextChangesValue() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('123')
-        }
-
-        statusBar.text = '123'
-
-        context.checking {
-            one(ansi).cursorLeft(3)
-            one(ansi).a('abc')
-        }
-
-        statusBar.text = 'abc'
-    }
-
-    @Test
-    public void redrawsStatusBarWhenTextChangesSuffix() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text 1')
-        }
-
-        statusBar.text = 'text 1'
-
-        context.checking {
-            one(ansi).cursorLeft(1)
-            one(ansi).a('2')
-        }
-
-        statusBar.text = 'text 2'
-    }
-
-    @Test
-    public void redrawsStatusBarWhenTextAdded() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text')
-        }
-
-        statusBar.text = 'text'
-
-        context.checking {
-            one(ansi).a(' 2')
-        }
-
-        statusBar.text = 'text 2'
-    }
-
-    @Test
-    public void redrawsStatusBarWhenTextRemoved() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text 1')
-        }
-
-        statusBar.text = 'text 1'
-
-        context.checking {
-            one(ansi).cursorLeft(3)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-        }
-
-        statusBar.text = 'tex'
-    }
-    
-    @Test
-    public void redrawsStatusBarWhenTextSetToEmpty() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text')
-        }
-
-        statusBar.text = 'text'
-
-        context.checking {
-            one(ansi).cursorLeft(4)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-        }
-
-        statusBar.text = ''
-    }
-
-    @Test
-    public void removesStatusBarWhenClosed() {
-        def statusBar = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('text')
-        }
-
-        statusBar.text = 'text'
-
-        context.checking {
-            one(ansi).cursorLeft(4)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-        }
-
-        statusBar.close();
-    }
-
-    @Test
-    public void showsMostRecentlyCreatedStatusBarOnly() {
-        context.checking {
-            one(ansi).a('first')
-        }
-
-        console.addStatusBar().text = 'first'
-
-        context.checking {
-            one(ansi).cursorLeft(5)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-        }
-
-        Label second = console.addStatusBar()
-
-        context.checking {
-            one(ansi).a('second')
-        }
-
-        second.text = 'second'
-
-        context.checking {
-            one(ansi).cursorLeft(6)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-            one(ansi).a('first')
-        }
-
-        second.close()
-    }
-
-    @Test
-    public void appendsTextWhenStatusBarIsPresent() {
-        context.checking {
-            one(ansi).a('status')
-        }
-
-        console.addStatusBar().text = 'status'
-
-        context.checking {
-            one(ansi).cursorLeft(6)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-            one(ansi).a('message')
-            one(ansi).a(EOL)
-            one(ansi).a('status')
-        }
-
-        console.mainArea.append("message$EOL");
-    }
-
-    @Test
-    public void appendsTextWithNoEOLWhenStatusBarIsPresent() {
-        context.checking {
-            one(ansi).a('status')
-        }
-
-        console.addStatusBar().text = 'status'
-
-        context.checking {
-            one(ansi).cursorLeft(6)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-            one(ansi).a('message')
-            one(ansi).newline()
-            one(ansi).a('status')
-        }
-
-        console.mainArea.append('message');
-
-        context.checking {
-            one(ansi).cursorLeft(6)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-            one(ansi).cursorUp(1)
-            one(ansi).cursorRight(7)
-            one(ansi).a('message2')
-            one(ansi).newline()
-            one(ansi).a('status')
-        }
-
-        console.mainArea.append('message2');
-    }
-
-    @Test
-    public void addsStatusBarWhenNoTrailingEOLInMainArea() {
-        context.checking {
-            one(ansi).a('message')
-        }
-
-        console.mainArea.append('message')
-
-        context.checking {
-            one(ansi).newline()
-            one(ansi).a('status')
-        }
-
-        console.addStatusBar().text = 'status'
-
-        context.checking {
-            one(ansi).cursorLeft(6)
-            one(ansi).eraseLine(Ansi.Erase.FORWARD)
-            one(ansi).cursorUp(1)
-            one(ansi).cursorRight(7)
-            one(ansi).a('message2')
-            one(ansi).a(EOL)
-            one(ansi).a('status')
-        }
-
-        console.mainArea.append("message2${EOL}")
-    }
-}
-
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/BasicProgressLoggingAwareFormatterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/BasicProgressLoggingAwareFormatterTest.groovy
deleted file mode 100644
index e93132c..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/BasicProgressLoggingAwareFormatterTest.groovy
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-
-
-package org.gradle.logging
-
-import ch.qos.logback.classic.LoggerContext
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.classic.spi.IThrowableProxy
-import ch.qos.logback.classic.spi.ThrowableProxy
-import org.gradle.api.logging.Logging
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.slf4j.LoggerFactory
-import org.slf4j.Marker
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
-import ch.qos.logback.classic.Level
-import org.gradle.api.logging.StandardOutputListener
-
- at RunWith(JMock.class)
-class BasicProgressLoggingAwareFormatterTest {
-    private static final String EOL = String.format('%n')
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final StandardOutputListener infoMessage = context.mock(StandardOutputListener.class)
-    private final StandardOutputListener errorMessage = context.mock(StandardOutputListener.class)
-    private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory()
-    private final BasicProgressLoggingAwareFormatter formatter = new BasicProgressLoggingAwareFormatter(loggerContext, infoMessage, errorMessage)
-
-    @Test
-    public void logsEventWithMessage() {
-        context.checking {
-            one(infoMessage).onOutput(String.format('message%n'))
-        }
-
-        formatter.format(event('message'))
-    }
-
-    @Test
-    public void logsEventWithMessageAndException() {
-        context.checking {
-            one(infoMessage).onOutput(withParam(allOf(startsWith(String.format('message%n')), containsString('java.lang.RuntimeException: broken'))))
-        }
-
-        formatter.format(event('message', new RuntimeException('broken')))
-    }
-
-    @Test
-    public void logsEventWithErrorMessage() {
-        context.checking {
-            one(errorMessage).onOutput(String.format('message%n'))
-        }
-
-        formatter.format(event('message', Level.ERROR))
-    }
-
-    @Test
-    public void logsProgressMessages() {
-        context.checking {
-            one(infoMessage).onOutput('description')
-            one(infoMessage).onOutput(' ')
-            one(infoMessage).onOutput('complete')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-        formatter.format(event('complete', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void ignoresProgressStatusMessages() {
-        context.checking {
-            one(infoMessage).onOutput('description')
-            one(infoMessage).onOutput(' ')
-            one(infoMessage).onOutput('complete')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-        formatter.format(event('tick', Logging.PROGRESS))
-        formatter.format(event('tick', Logging.PROGRESS))
-        formatter.format(event('complete', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsNestedProgressMessages() {
-        context.checking {
-            one(infoMessage).onOutput('description1')
-            one(infoMessage).onOutput(EOL)
-            one(infoMessage).onOutput('description2')
-            one(infoMessage).onOutput(' ')
-            one(infoMessage).onOutput('complete2')
-            one(infoMessage).onOutput(EOL)
-            one(infoMessage).onOutput('complete1')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('description1', Logging.PROGRESS_STARTED))
-        formatter.format(event('description2', Logging.PROGRESS_STARTED))
-        formatter.format(event('tick', Logging.PROGRESS))
-        formatter.format(event('tick', Logging.PROGRESS))
-        formatter.format(event('complete2', Logging.PROGRESS_COMPLETE))
-        formatter.format(event('complete1', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsMixOfProgressAndInfoMessages() {
-        context.checking {
-            one(infoMessage).onOutput('description')
-            one(infoMessage).onOutput(EOL)
-            one(infoMessage).onOutput(String.format('message%n'))
-            one(infoMessage).onOutput('complete')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-        formatter.format(event('tick', Logging.PROGRESS))
-        formatter.format(event('message'))
-        formatter.format(event('tick', Logging.PROGRESS))
-        formatter.format(event('complete', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsMixOfProgressAndErrorMessages() {
-        context.checking {
-            one(infoMessage).onOutput('description')
-            one(infoMessage).onOutput(EOL)
-            one(errorMessage).onOutput(String.format('message%n'))
-            one(infoMessage).onOutput('complete')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-        formatter.format(event('message', Level.ERROR))
-        formatter.format(event('complete', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithNoCompletionStatus() {
-        context.checking {
-            one(infoMessage).onOutput('description')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-        formatter.format(event('', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithNoCompletionStatusAndOtherMessages() {
-        context.checking {
-            one(infoMessage).onOutput('description')
-            one(infoMessage).onOutput(EOL)
-            one(infoMessage).onOutput(String.format('message%n'))
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-        formatter.format(event('message'))
-        formatter.format(event('', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithNoStartStatus() {
-        formatter.format(event('', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(infoMessage).onOutput('done')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('done', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsNestedProgressMessagesWithNoStartStatusAndOtherMessages() {
-        context.checking {
-            one(infoMessage).onOutput('outer')
-        }
-
-        formatter.format(event('outer', Logging.PROGRESS_STARTED))
-
-        formatter.format(event('', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(infoMessage).onOutput(EOL)
-            one(errorMessage).onOutput(String.format('message%n'))
-        }
-
-        formatter.format(event('message', Level.ERROR))
-
-        context.checking {
-            one(infoMessage).onOutput('done inner')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('done inner', Logging.PROGRESS_COMPLETE))
-
-        context.checking {
-            one(infoMessage).onOutput('done outer')
-            one(infoMessage).onOutput(EOL)
-        }
-
-        formatter.format(event('done outer', Logging.PROGRESS_COMPLETE))
-    }
-
-    private ILoggingEvent event(String text, Marker marker) {
-        event(text, null, marker)
-    }
-
-    private ILoggingEvent event(String text, Level level) {
-        event(text, null, null, level)
-    }
-
-    private ILoggingEvent event(String text, Throwable failure = null, marker = null, Level level = Level.INFO) {
-        IThrowableProxy throwableProxy = failure == null ? null : new ThrowableProxy(failure)
-        [
-                getLevel: {level},
-                getThrowableProxy: {throwableProxy},
-                getFormattedMessage: {text},
-                getMarker: {marker}
-        ] as ILoggingEvent
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/ConsoleBackedFormatterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/ConsoleBackedFormatterTest.groovy
deleted file mode 100644
index 19b8957..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/ConsoleBackedFormatterTest.groovy
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-
-
-
-
-package org.gradle.logging
-
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.classic.spi.IThrowableProxy
-import ch.qos.logback.classic.spi.ThrowableProxy
-import org.gradle.api.logging.Logging
-import org.gradle.util.JUnit4GroovyMockery
-import org.jmock.integration.junit4.JMock
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.slf4j.Marker
-import static org.junit.Assert.*
-import static org.hamcrest.Matchers.*
-
-import org.slf4j.LoggerFactory
-import ch.qos.logback.classic.LoggerContext
-import org.junit.Before
-import ch.qos.logback.classic.Level
-
- at RunWith(JMock.class)
-class ConsoleBackedFormatterTest {
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
-    private final Console console = context.mock(Console.class)
-    private final TextArea mainArea = context.mock(TextArea.class)
-    private final Label statusBar = context.mock(Label.class)
-    private ConsoleBackedFormatter formatter
-
-    @Before
-    public void setup() {
-        context.checking {
-            one(console).addStatusBar()
-            will(returnValue(statusBar))
-            allowing(console).getMainArea()
-            will(returnValue(mainArea))
-        }
-
-        formatter = new ConsoleBackedFormatter((LoggerContext) LoggerFactory.getILoggerFactory(), console)
-    }
-
-    @Test
-    public void logsEventWithMessage() {
-        context.checking {
-            one(mainArea).append(String.format('message%n'))
-        }
-
-        formatter.format(event('message'))
-    }
-
-    @Test
-    public void logsEventWithMessageAndException() {
-        context.checking {
-            one(mainArea).append(withParam(anything()))
-            will { message ->
-                assertThat(message.toString(), startsWith(String.format('message%n')))
-                assertThat(message.toString(), containsString('java.lang.RuntimeException: broken'))
-            }
-        }
-
-        formatter.format(event('message', new RuntimeException('broken')))
-    }
-
-    @Test
-    public void logsErrorMessage() {
-        context.checking {
-            one(mainArea).append(String.format('message%n'))
-        }
-
-        formatter.format(event('message', Level.ERROR))
-    }
-
-    @Test
-    public void logsProgressMessages() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(statusBar).setText('')
-            one(mainArea).append(String.format('description complete%n'))
-        }
-        
-        formatter.format(event('complete', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressStatusMessages() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(statusBar).setText('> status')
-        }
-
-        formatter.format(event('status', Logging.PROGRESS))
-    }
-
-    @Test
-    public void logsNestedProgressMessages() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description1', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(mainArea).append(String.format('description1%n'))
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description2', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(statusBar).setText('> tick')
-        }
-
-        formatter.format(event('tick', Logging.PROGRESS))
-
-        context.checking {
-            one(statusBar).setText('')
-            one(mainArea).append(String.format('description2 complete2%n'))
-        }
-
-        formatter.format(event('complete2', Logging.PROGRESS_COMPLETE))
-
-        context.checking {
-            one(statusBar).setText('')
-            one(mainArea).append(String.format('description1 complete1%n'))
-        }
-
-        formatter.format(event('complete1', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsMixOfProgressAndOtherMessages() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(mainArea).append(String.format('description%n'))
-            one(mainArea).append(String.format('message%n'))
-        }
-
-        formatter.format(event('message'))
-
-        context.checking {
-            one(statusBar).setText('')
-            one(mainArea).append(String.format('description complete%n'))
-        }
-
-        formatter.format(event('complete', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithEmptyCompletionStatus() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(mainArea).append(String.format('description%n'))
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithEmptyCompletionStatusAndOtherMessages() {
-
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('description', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(mainArea).append(String.format('description%n'))
-            one(mainArea).append(String.format('message%n'))
-        }
-
-        formatter.format(event('message'))
-
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithEmptyStartAndCompletionStatus() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(statusBar).setText('> running')
-        }
-        formatter.format(event('running', Logging.PROGRESS))
-
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('', Logging.PROGRESS_COMPLETE))
-    }
-
-    @Test
-    public void logsProgressMessagesWithEmptyStartAndCompletionStatusAndOtherMessages() {
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('', Logging.PROGRESS_STARTED))
-
-        context.checking {
-            one(mainArea).append(String.format('message%n'))
-        }
-
-        formatter.format(event('message'))
-
-        context.checking {
-            one(statusBar).setText('')
-        }
-
-        formatter.format(event('', Logging.PROGRESS_COMPLETE))
-    }
-    
-    private Label statusBar() {
-        return context.mock(Label.class)
-    }
-
-    private ILoggingEvent event(String text, Marker marker) {
-        event(text, null, marker)
-    }
-
-    private ILoggingEvent event(String text, Level level) {
-        event(text, null, null, level)
-    }
-
-    private ILoggingEvent event(String text, Throwable failure = null, marker = null, Level level = Level.INFO) {
-        IThrowableProxy throwableProxy = failure == null ? null : new ThrowableProxy(failure)
-        [getLevel: {level}, getThrowableProxy: {throwableProxy}, getFormattedMessage: {text}, getMarker: {marker}] as ILoggingEvent
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultLoggingManagerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultLoggingManagerTest.java
deleted file mode 100644
index 7a11312..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultLoggingManagerTest.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging;
-
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.LoggingOutput;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.util.JUnit4GroovyMockery;
-import org.gradle.util.RedirectStdOutAndErr;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock.class)
-public class DefaultLoggingManagerTest {
-    @Rule
-    public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr();
-    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery();
-    private final LoggingSystem loggingSystem = context.mock(LoggingSystem.class);
-    private final LoggingSystem stdOutLoggingSystem = context.mock(LoggingSystem.class);
-    private final LoggingSystem stdErrLoggingSystem = context.mock(LoggingSystem.class);
-    private final LoggingOutput loggingOutput = context.mock(LoggingOutput.class);
-    private final DefaultLoggingManager loggingManager = new DefaultLoggingManager(loggingSystem, stdOutLoggingSystem, stdErrLoggingSystem, loggingOutput);
-
-    @Test
-    public void defaultValues() {
-        assertTrue(loggingManager.isStandardOutputCaptureEnabled());
-        assertEquals(LogLevel.QUIET, loggingManager.getStandardOutputCaptureLevel());
-        assertEquals(LogLevel.ERROR, loggingManager.getStandardErrorCaptureLevel());
-        assertNull(loggingManager.getLevel());
-    }
-
-    @Test
-    public void canChangeStdOutCaptureLogLevel() {
-        loggingManager.captureStandardOutput(LogLevel.ERROR);
-        assertTrue(loggingManager.isStandardOutputCaptureEnabled());
-        assertEquals(LogLevel.ERROR, loggingManager.getStandardOutputCaptureLevel());
-    }
-
-    @Test
-    public void canChangeStdErrCaptureLogLevel() {
-        loggingManager.captureStandardError(LogLevel.WARN);
-        assertEquals(LogLevel.WARN, loggingManager.getStandardErrorCaptureLevel());
-    }
-
-    @Test
-    public void canChangeLogLevel() {
-        loggingManager.setLevel(LogLevel.ERROR);
-        assertEquals(LogLevel.ERROR, loggingManager.getLevel());
-    }
-
-    @Test
-    public void canDisableCapture() {
-        loggingManager.disableStandardOutputCapture();
-        assertFalse(loggingManager.isStandardOutputCaptureEnabled());
-        assertNull(loggingManager.getStandardOutputCaptureLevel());
-    }
-
-    @Test
-    public void startStopWithCaptureDisabled() {
-        loggingManager.disableStandardOutputCapture();
-
-        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            one(stdOutLoggingSystem).off();
-            will(returnValue(stdOutSnapshot));
-            one(stdErrLoggingSystem).off();
-            will(returnValue(stdErrSnapshot));
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).restore(stdOutSnapshot);
-            one(stdErrLoggingSystem).restore(stdErrSnapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void startStopWithCaptureEnabled() {
-        loggingManager.captureStandardOutput(LogLevel.DEBUG);
-        loggingManager.captureStandardError(LogLevel.INFO);
-
-        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
-            will(returnValue(stdOutSnapshot));
-            one(stdErrLoggingSystem).on(LogLevel.INFO);
-            will(returnValue(stdErrSnapshot));
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).restore(stdOutSnapshot);
-            one(stdErrLoggingSystem).restore(stdErrSnapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void startStopWithLogLevelSet() {
-        loggingManager.setLevel(LogLevel.DEBUG);
-
-        final LoggingSystem.Snapshot snapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(stdOutLoggingSystem);
-            ignoring(stdErrLoggingSystem);
-            one(loggingSystem).on(LogLevel.DEBUG);
-            will(returnValue(snapshot));
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(loggingSystem).restore(snapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void startStopWithLogLevelNotSet() {
-        final LoggingSystem.Snapshot snapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(stdOutLoggingSystem);
-            ignoring(stdErrLoggingSystem);
-            one(loggingSystem).snapshot();
-            will(returnValue(snapshot));
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(loggingSystem).restore(snapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void disableCaptureWhileStarted() {
-        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
-            will(returnValue(stdOutSnapshot));
-            one(stdErrLoggingSystem).on(LogLevel.INFO);
-            will(returnValue(stdErrSnapshot));
-        }});
-
-        loggingManager.captureStandardOutput(LogLevel.DEBUG);
-        loggingManager.captureStandardError(LogLevel.INFO);
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).off();
-            one(stdErrLoggingSystem).off();
-        }});
-
-        loggingManager.disableStandardOutputCapture();
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).restore(stdOutSnapshot);
-            one(stdErrLoggingSystem).restore(stdErrSnapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void enableCaptureWhileStarted() {
-        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            one(stdOutLoggingSystem).off();
-            will(returnValue(stdOutSnapshot));
-            one(stdErrLoggingSystem).off();
-            will(returnValue(stdErrSnapshot));
-        }});
-
-        loggingManager.disableStandardOutputCapture();
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
-            one(stdErrLoggingSystem).on(LogLevel.INFO);
-        }});
-
-        loggingManager.captureStandardOutput(LogLevel.DEBUG);
-        loggingManager.captureStandardError(LogLevel.INFO);
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).restore(stdOutSnapshot);
-            one(stdErrLoggingSystem).restore(stdErrSnapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void changeCaptureLevelWhileStarted() {
-        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
-            will(returnValue(stdOutSnapshot));
-            one(stdErrLoggingSystem).on(LogLevel.DEBUG);
-            will(returnValue(stdErrSnapshot));
-        }});
-
-        loggingManager.captureStandardOutput(LogLevel.DEBUG);
-        loggingManager.captureStandardError(LogLevel.DEBUG);
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).on(LogLevel.WARN);
-        }});
-
-        loggingManager.captureStandardOutput(LogLevel.WARN);
-
-        context.checking(new Expectations() {{
-            one(stdOutLoggingSystem).restore(stdOutSnapshot);
-            one(stdErrLoggingSystem).restore(stdErrSnapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void changeLogLevelWhileStarted() {
-        final LoggingSystem.Snapshot snapshot = context.mock(LoggingSystem.Snapshot.class);
-        context.checking(new Expectations() {{
-            ignoring(stdOutLoggingSystem);
-            ignoring(stdErrLoggingSystem);
-            one(loggingSystem).snapshot();
-            will(returnValue(snapshot));
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            ignoring(stdOutLoggingSystem);
-            one(loggingSystem).on(LogLevel.LIFECYCLE);
-            will(returnValue(context.mock(LoggingSystem.Snapshot.class)));
-        }});
-
-        loggingManager.setLevel(LogLevel.LIFECYCLE);
-
-        context.checking(new Expectations() {{
-            one(loggingSystem).restore(snapshot);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void addsListenersOnStartAndRemovesOnStop() {
-        final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
-        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
-
-        loggingManager.addStandardOutputListener(stdoutListener);
-        loggingManager.addStandardErrorListener(stderrListener);
-
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            ignoring(stdOutLoggingSystem);
-            ignoring(stdErrLoggingSystem);
-            one(loggingOutput).addStandardOutputListener(stdoutListener);
-            one(loggingOutput).addStandardErrorListener(stderrListener);
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(loggingOutput).removeStandardOutputListener(stdoutListener);
-            one(loggingOutput).removeStandardErrorListener(stderrListener);
-        }});
-
-        loggingManager.stop();
-    }
-
-    @Test
-    public void addsListenersWhileStarted() {
-        final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
-        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
-
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            ignoring(stdOutLoggingSystem);
-            ignoring(stdErrLoggingSystem);
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(loggingOutput).addStandardOutputListener(stdoutListener);
-            one(loggingOutput).addStandardErrorListener(stderrListener);
-        }});
-
-        loggingManager.addStandardOutputListener(stdoutListener);
-        loggingManager.addStandardErrorListener(stderrListener);
-    }
-
-    @Test
-    public void removesListenersWhileStarted() {
-        final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
-        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
-
-        loggingManager.addStandardOutputListener(stdoutListener);
-        loggingManager.addStandardErrorListener(stderrListener);
-
-        context.checking(new Expectations() {{
-            ignoring(loggingSystem);
-            ignoring(stdOutLoggingSystem);
-            ignoring(stdErrLoggingSystem);
-            one(loggingOutput).addStandardOutputListener(stdoutListener);
-            one(loggingOutput).addStandardErrorListener(stderrListener);
-        }});
-
-        loggingManager.start();
-
-        context.checking(new Expectations() {{
-            one(loggingOutput).removeStandardOutputListener(stdoutListener);
-            one(loggingOutput).removeStandardErrorListener(stderrListener);
-        }});
-
-        loggingManager.removeStandardOutputListener(stdoutListener);
-        loggingManager.removeStandardErrorListener(stderrListener);
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultProgressLoggerFactoryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultProgressLoggerFactoryTest.groovy
deleted file mode 100644
index a8b53ca..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultProgressLoggerFactoryTest.groovy
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-
-package org.gradle.logging
-
-import spock.lang.Specification
-import org.gradle.listener.ListenerManager
-
-class DefaultProgressLoggerFactoryTest extends Specification {
-    private final ListenerManager listenerManager = Mock()
-    private final ProgressListener progressListener = Mock()
-    private final ProgressLoggerFactory factory = new DefaultProgressLoggerFactory(listenerManager)
-
-    def progressLoggerBroadcastsEvents() {
-        when:
-        def logger = factory.start('description')
-
-        then:
-        logger != null
-        1 * listenerManager.getBroadcaster(ProgressListener.class) >> progressListener
-        1 * progressListener.started(!null)
-
-        when:
-        logger.progress('progress')
-
-        then:
-        1 * progressListener.progress(!null)
-
-        when:
-        logger.completed()
-
-        then:
-        1 * progressListener.completed(!null)
-    }
-
-    def hasEmptyStatusOnStart() {
-        when:
-        def logger = factory.start('description')
-
-        then:
-        1 * listenerManager.getBroadcaster(ProgressListener.class) >> progressListener
-        logger.description == 'description'
-        logger.status == ''
-    }
-
-    def hasMostRecentStatusOnProgress() {
-        when:
-        def logger = factory.start('description')
-        logger.progress('status')
-
-        then:
-        1 * listenerManager.getBroadcaster(ProgressListener.class) >> progressListener
-        logger.status == 'status'
-    }
-    
-    def hasMostRecentStatusOnComplete() {
-        when:
-        def logger = factory.start('description')
-        logger.completed('done')
-
-        then:
-        1 * listenerManager.getBroadcaster(ProgressListener.class) >> progressListener
-        logger.status == 'done'
-    }
-}
-
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultStandardOutputRedirectorTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultStandardOutputRedirectorTest.groovy
deleted file mode 100644
index 080faf9..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/DefaultStandardOutputRedirectorTest.groovy
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.logging
-
-import org.gradle.api.logging.StandardOutputListener
-import org.gradle.util.RedirectStdOutAndErr
-import org.junit.Rule
-import spock.lang.Specification
-
-class DefaultStandardOutputRedirectorTest extends Specification {
-    private static final String EOL = System.getProperty('line.separator')
-    @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
-    private final DefaultStandardOutputRedirector redirector = new DefaultStandardOutputRedirector()
-    private final StandardOutputListener stdOutListener = Mock()
-    private final StandardOutputListener stdErrListener = Mock()
-
-    def startAndStopDoesNothingWhenNothingRedirected() {
-        when:
-        redirector.start()
-        System.out.println('this is stdout')
-        System.err.println('this is stderr')
-        redirector.stop()
-
-        then:
-        System.out == outputs.stdOutPrintStream
-        System.err == outputs.stdErrPrintStream
-    }
-    
-    def startAndStopRedirectsStdOut() {
-        when:
-        redirector.redirectStandardOutputTo(stdOutListener)
-        redirector.start()
-        System.out.println('this is stdout')
-        System.err.println('this is stderr')
-        redirector.stop()
-
-        then:
-        1 * stdOutListener.onOutput('this is stdout' + EOL)
-        0 * stdOutListener._
-        System.out == outputs.stdOutPrintStream
-        System.err == outputs.stdErrPrintStream
-    }
-
-    def startAndStopRedirectsStdErr() {
-        when:
-        redirector.redirectStandardErrorTo(stdErrListener)
-        redirector.start()
-        System.out.println('this is stdout')
-        System.err.println('this is stderr')
-        redirector.stop()
-
-        then:
-        1 * stdErrListener.onOutput('this is stderr' + EOL)
-        0 * stdErrListener._
-        System.out == outputs.stdOutPrintStream
-        System.err == outputs.stdErrPrintStream
-    }
-    
-    def canRedirectMultipleTimes() {
-        when:
-        redirector.redirectStandardErrorTo(stdErrListener)
-        redirector.start()
-        redirector.stop()
-        redirector.redirectStandardOutputTo(stdOutListener)
-        redirector.start()
-        System.out.println('this is stdout')
-        System.err.println('this is stderr')
-        redirector.stop()
-
-        then:
-        1 * stdOutListener.onOutput('this is stdout' + EOL)
-        0 * stdOutListener._
-        0 * stdErrListener._
-        System.out == outputs.stdOutPrintStream
-        System.err == outputs.stdErrPrintStream
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/JavaUtilLoggingConfigurerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/JavaUtilLoggingConfigurerTest.groovy
deleted file mode 100644
index 562e95f..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/JavaUtilLoggingConfigurerTest.groovy
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging
-
-import spock.lang.Specification
-import java.util.logging.LogManager
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.core.Appender
-import org.gradle.api.logging.LogLevel
-import java.util.logging.Logger
-import ch.qos.logback.classic.Level
-
-class JavaUtilLoggingConfigurerTest extends Specification {
-    private final Appender<ILoggingEvent> appender = Mock()
-    private final LoggingTestHelper helper = new LoggingTestHelper(appender)
-    private final JavaUtilLoggingConfigurer configurer = new JavaUtilLoggingConfigurer()
-
-    def setup() {
-        helper.attachAppender()
-    }
-
-    def cleanup() {
-        helper.detachAppender()
-        LogManager.getLogManager().reset()
-    }
-
-    def routesJulToSlf4j() {
-        when:
-        configurer.configure(LogLevel.DEBUG)
-        Logger.getLogger('test').info('info message')
-
-        then:
-        1 * appender.doAppend({ILoggingEvent event -> event.level == Level.INFO && event.message == 'info message'})
-        0 * appender._
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
index 38d4e0d..e95dae0 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingServiceRegistryTest.groovy
@@ -16,14 +16,37 @@
 
 package org.gradle.logging
 
+import org.gradle.logging.internal.DefaultLoggingManagerFactory
+import org.gradle.logging.internal.DefaultProgressLoggerFactory
+import org.gradle.logging.internal.DefaultStyledTextOutputFactory
 import spock.lang.Specification
+import org.gradle.initialization.CommandLineConverter
+import org.gradle.logging.internal.LoggingCommandLineConverter
 
 class LoggingServiceRegistryTest extends Specification {
     private final LoggingServiceRegistry registry = new LoggingServiceRegistry()
     
     def providesALoggingManagerFactory() {
         expect:
-        def factory = registry.get(LoggingManagerFactory.class)
+        def factory = registry.getFactory(LoggingManagerInternal.class)
         factory instanceof DefaultLoggingManagerFactory
     }
+
+    def providesAStyledTextOutputFactory() {
+        expect:
+        def factory = registry.get(StyledTextOutputFactory.class)
+        factory instanceof DefaultStyledTextOutputFactory
+    }
+    
+    def providesAProgressLoggerFactory() {
+        expect:
+        def factory = registry.get(ProgressLoggerFactory.class)
+        factory instanceof DefaultProgressLoggerFactory
+    }
+
+    def providesACommandLineConverter() {
+        expect:
+        def converter = registry.get(CommandLineConverter.class)
+        converter instanceof LoggingCommandLineConverter
+    }
 }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingSystemAdapterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingSystemAdapterTest.groovy
deleted file mode 100644
index 1cc962a..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/LoggingSystemAdapterTest.groovy
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging
-
-import spock.lang.Specification
-
-import org.gradle.api.logging.LogLevel
-
-class LoggingSystemAdapterTest extends Specification {
-    private final LoggingConfigurer loggingConfigurer = Mock()
-    private final LoggingSystemAdapter loggingSystem = new LoggingSystemAdapter(loggingConfigurer)
-
-    def onUsesLoggingConfigurerToSetLoggingLevel() {
-        when:
-        loggingSystem.on(LogLevel.DEBUG)
-
-        then:
-        1 * loggingConfigurer.configure(LogLevel.DEBUG)
-        0 * loggingConfigurer._
-    }
-
-    def offDoesNothing() {
-        when:
-        loggingSystem.off()
-
-        then:
-        0 * loggingConfigurer._
-    }
-
-    def restoreSetsLoggingLevelToPreviousLoggingLevel() {
-        when:
-        def snapshot = loggingSystem.snapshot()
-        loggingSystem.restore(snapshot)
-
-        then:
-        1 * loggingConfigurer.configure(LogLevel.LIFECYCLE)
-    }
-    
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/PrintStreamLoggingSystemTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/PrintStreamLoggingSystemTest.groovy
deleted file mode 100644
index facb5d0..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/PrintStreamLoggingSystemTest.groovy
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging
-
-import ch.qos.logback.classic.Level
-import ch.qos.logback.classic.spi.ILoggingEvent
-import ch.qos.logback.core.Appender
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.logging.Logging
-import spock.lang.Specification
-
-class PrintStreamLoggingSystemTest extends Specification {
-    private final Appender<ILoggingEvent> appender = Mock()
-    private final LoggingTestHelper helper = new LoggingTestHelper(appender)
-    private OutputStream original = new ByteArrayOutputStream()
-    private PrintStream stream = new PrintStream(original)
-    private final PrintStreamLoggingSystem loggingSystem = new PrintStreamLoggingSystem(Logging.getLogger('logger')) {
-        protected PrintStream get() {
-            stream
-        }
-
-        protected void set(PrintStream printStream) {
-            stream = printStream
-        }
-    }
-
-    def setup() {
-        helper.attachAppender()
-    }
-    
-    def teardown() {
-        helper.detachAppender()
-    }
-
-    def onStartsCapturingWhenNotAlreadyCapturing() {
-        when:
-        loggingSystem.on(LogLevel.INFO)
-        stream.println('info')
-
-        then:
-        1 * appender.doAppend({ILoggingEvent event -> event.level == Level.INFO && event.message == 'info'})
-        original.toString() == ''
-        0 * appender._
-    }
-
-    def onChangesLogLevelsWhenAlreadyCapturing() {
-        loggingSystem.on(LogLevel.INFO)
-
-        when:
-        loggingSystem.on(LogLevel.DEBUG)
-        stream.println('info')
-
-        then:
-        1 * appender.doAppend({ILoggingEvent event -> event.level == Level.DEBUG && event.message == 'info'})
-        original.toString() == ''
-        0 * appender._
-    }
-
-    def offDoesNothingWhenNotAlreadyCapturing() {
-        when:
-        loggingSystem.off()
-        stream.println('info')
-
-        then:
-        original.toString() == String.format('info%n')
-        0 * appender._
-    }
-
-    def offStopsCapturingWhenAlreadyCapturing() {
-        loggingSystem.on(LogLevel.WARN)
-
-        when:
-        loggingSystem.off()
-
-        stream.println('info')
-
-        then:
-        original.toString() == String.format('info%n')
-        0 * appender._
-    }
-
-    def restoreStopsCapturingWhenCapturingWasNotInstalledWhenSnapshotTaken() {
-        def snapshot = loggingSystem.snapshot()
-        loggingSystem.on(LogLevel.ERROR)
-
-        when:
-        loggingSystem.restore(snapshot)
-        stream.println('info')
-
-        then:
-        original.toString() == String.format('info%n')
-        0 * appender._
-    }
-
-    def restoreStopsCapturingWhenCapturingWasOffWhenSnapshotTaken() {
-        loggingSystem.on(LogLevel.INFO)
-        loggingSystem.off()
-        def snapshot = loggingSystem.snapshot()
-        loggingSystem.on(LogLevel.ERROR)
-
-        when:
-        loggingSystem.restore(snapshot)
-        stream.println('info')
-
-        then:
-        original.toString() == String.format('info%n')
-        0 * appender._
-    }
-
-    def restoreStartsCapturingWhenCapturingWasOnWhenSnapshotTaken() {
-        loggingSystem.on(LogLevel.WARN)
-        def snapshot = loggingSystem.snapshot()
-        loggingSystem.off()
-
-        when:
-        loggingSystem.restore(snapshot)
-        stream.println('info')
-
-        then:
-        1 * appender.doAppend({ILoggingEvent event -> event.level == Level.WARN && event.message == 'info'})
-        original.toString() == ''
-        0 * appender._
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/Slf4jLoggingConfigurerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/Slf4jLoggingConfigurerTest.java
deleted file mode 100644
index 14b19cb..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/Slf4jLoggingConfigurerTest.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.logging;
-
-import ch.qos.logback.classic.LoggerContext;
-import org.gradle.api.logging.LogLevel;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.api.specs.Spec;
-import org.gradle.util.RedirectStdOutAndErr;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.FileDescriptor;
-import java.io.StringWriter;
-
-import static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-public class Slf4jLoggingConfigurerTest {
-    private final TerminalDetectorStub terminalDetector = new TerminalDetectorStub();
-    private final ConsoleStub console = new ConsoleStub();
-    private final Slf4jLoggingConfigurer configurer = new Slf4jLoggingConfigurer(terminalDetector) {
-        @Override
-        Console createConsole() {
-            return console;
-        }
-    };
-    private final StandardOutputListener outputListener = new ListenerImpl();
-    private final StandardOutputListener errorListener = new ListenerImpl();
-    private final Logger logger = LoggerFactory.getLogger("cat1");
-    @Rule
-    public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr();
-
-    @Before
-    public void setUp() {
-        configurer.addStandardOutputListener(outputListener);
-        configurer.addStandardErrorListener(errorListener);
-    }
-
-    @After
-    public void tearDown() {
-        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
-        lc.reset();
-    }
-
-    @Test
-    public void canListenOnStdOutput() {
-        configurer.configure(LogLevel.INFO);
-
-        logger.debug("debug message");
-        logger.info("info message");
-        logger.warn("warn message");
-        logger.error("error message");
-
-        assertThat(outputListener.toString(), equalTo(String.format("info message%nwarn message%n")));
-    }
-
-    @Test
-    public void canListenOnStdError() {
-        configurer.configure(LogLevel.INFO);
-
-        logger.debug("debug message");
-        logger.info("info message");
-        logger.warn("warn message");
-        logger.error("error message");
-
-        assertThat(errorListener.toString(), equalTo(String.format("error message%n")));
-    }
-
-    @Test
-    public void filtersProgressAndLowerWhenConfiguredAtQuietLevel() {
-        configurer.configure(LogLevel.QUIET);
-
-        logger.info(Logging.QUIET, "quiet message");
-        logger.info(Logging.PROGRESS_STARTED, "<start>");
-        logger.info(Logging.PROGRESS, "<progress>");
-        logger.info(Logging.PROGRESS_COMPLETE, "<complete>");
-        logger.info(Logging.LIFECYCLE, "lifecycle message");
-        logger.info("info message");
-        logger.debug("debug message");
-
-        assertThat(outputListener.toString(), equalTo(String.format("quiet message%n")));
-    }
-
-    @Test
-    public void filtersInfoAndLowerWhenConfiguredAtLifecycleLevel() {
-        configurer.configure(LogLevel.LIFECYCLE);
-
-        logger.info(Logging.QUIET, "quiet message");
-        logger.info(Logging.LIFECYCLE, "lifecycle message");
-        logger.info("info message");
-        logger.debug("debug message");
-
-        assertThat(outputListener.toString(), equalTo(String.format("quiet message%nlifecycle message%n")));
-    }
-
-    @Test
-    public void filtersDebugAndLowerWhenConfiguredAtInfoLevel() {
-        configurer.configure(LogLevel.INFO);
-
-        logger.info(Logging.QUIET, "quiet message");
-        logger.info(Logging.LIFECYCLE, "lifecycle message");
-        logger.info("info message");
-        logger.debug("debug message");
-
-        assertThat(outputListener.toString(), equalTo(String.format(
-                "quiet message%nlifecycle message%ninfo message%n")));
-    }
-
-    @Test
-    public void formatsProgressLogMessages() {
-        configurer.configure(LogLevel.LIFECYCLE);
-
-        logger.info(Logging.PROGRESS_STARTED, "<start>");
-        logger.info(Logging.PROGRESS, "<tick>");
-        logger.info(Logging.PROGRESS, "<tick>");
-        logger.info(Logging.PROGRESS_COMPLETE, "<complete>");
-
-        assertThat(outputListener.toString(), equalTo(String.format("<start> <complete>%n")));
-    }
-
-    @Test
-    public void formatsProgressLogMessagesForInfoLevel() {
-        configurer.configure(LogLevel.INFO);
-
-        logger.info(Logging.PROGRESS_STARTED, "<start>");
-        logger.info(Logging.PROGRESS, "<tick>");
-        logger.info(Logging.PROGRESS, "<tick>");
-        logger.info(Logging.PROGRESS_COMPLETE, "<complete>");
-
-        assertThat(outputListener.toString(), equalTo(String.format("<start> <complete>%n")));
-    }
-
-    @Test
-    public void formatsProgressLogMessagesForDebugLevel() {
-        configurer.configure(LogLevel.DEBUG);
-
-        logger.info(Logging.PROGRESS_STARTED, "<start>");
-        logger.info(Logging.PROGRESS, "<tick1>");
-        logger.info(Logging.PROGRESS, "<tick2>");
-        logger.info(Logging.PROGRESS_COMPLETE, "<complete>");
-
-        assertThat(outputListener.toString(), containsLine(endsWith(String.format("<start>"))));
-        assertThat(outputListener.toString(), containsLine(endsWith(String.format("<tick1>"))));
-        assertThat(outputListener.toString(), containsLine(endsWith(String.format("<tick2>"))));
-        assertThat(outputListener.toString(), containsLine(endsWith(String.format("<complete>"))));
-    }
-
-    @Test
-    public void routesLoggingMessagesWhenStdOutAndStdErrAreTerminals() {
-        terminalDetector.stderrIsTerminal().stdoutIsTerminal();
-
-        configurer.configure(LogLevel.LIFECYCLE);
-
-        logger.info(Logging.LIFECYCLE, "lifecycle");
-        logger.error("error");
-
-        assertThat(outputListener.toString(), equalTo(String.format("lifecycle%n")));
-        assertThat(errorListener.toString(), equalTo(String.format("error%n")));
-        assertThat(console.toString(), equalTo(String.format("lifecycle%nerror%n")));
-        assertThat(outputs.getStdOut(), equalTo(""));
-        assertThat(outputs.getStdErr(), equalTo(""));
-    }
-
-    @Test
-    public void routesLoggingMessagesWhenStdOutIsTerminal() {
-        terminalDetector.stdoutIsTerminal();
-
-        configurer.configure(LogLevel.LIFECYCLE);
-
-        logger.info(Logging.LIFECYCLE, "lifecycle");
-        logger.error("error");
-
-        assertThat(outputListener.toString(), equalTo(String.format("lifecycle%n")));
-        assertThat(errorListener.toString(), equalTo(String.format("error%n")));
-        assertThat(console.toString(), equalTo(String.format("lifecycle%n")));
-        assertThat(outputs.getStdOut(), equalTo(""));
-        assertThat(outputs.getStdErr(), equalTo(String.format("error%n")));
-    }
-
-    @Test
-    public void routesLoggingMessagesWhenStdErrIsTerminal() {
-        terminalDetector.stderrIsTerminal();
-
-        configurer.configure(LogLevel.LIFECYCLE);
-
-        logger.info(Logging.LIFECYCLE, "lifecycle");
-        logger.error("error");
-
-        assertThat(outputListener.toString(), equalTo(String.format("lifecycle%n")));
-        assertThat(errorListener.toString(), equalTo(String.format("error%n")));
-        assertThat(console.toString(), equalTo(String.format("error%n")));
-        assertThat(outputs.getStdOut(), equalTo(String.format("lifecycle%n")));
-        assertThat(outputs.getStdErr(), equalTo(""));
-    }
-
-    @Test
-    public void routesLoggingMessagesWhenNeitherStdOutAndStdErrAreTerminals() {
-        configurer.configure(LogLevel.LIFECYCLE);
-
-        logger.info(Logging.LIFECYCLE, "lifecycle");
-        logger.error("error");
-
-        assertThat(outputListener.toString(), equalTo(String.format("lifecycle%n")));
-        assertThat(errorListener.toString(), equalTo(String.format("error%n")));
-        assertThat(console.toString(), equalTo(""));
-        assertThat(outputs.getStdOut(), equalTo(String.format("lifecycle%n")));
-        assertThat(outputs.getStdErr(), equalTo(String.format("error%n")));
-    }
-
-    @Test
-    public void doesNotUseTheConsoleWhenSetToDebugLevel() {
-        terminalDetector.stderrIsTerminal().stdoutIsTerminal();
-
-        configurer.configure(LogLevel.DEBUG);
-
-        logger.info(Logging.LIFECYCLE, "lifecycle");
-        logger.error("error");
-
-        assertThat(outputListener.toString(), containsLine(endsWith("lifecycle")));
-        assertThat(errorListener.toString(), containsLine(endsWith("error")));
-        assertThat(console.toString(), equalTo(""));
-        assertThat(outputs.getStdOut(), containsLine(endsWith("lifecycle")));
-        assertThat(outputs.getStdErr(), containsLine(endsWith("error")));
-    }
-
-    private static class ListenerImpl implements StandardOutputListener {
-        private final StringWriter writer = new StringWriter();
-
-        @Override
-        public String toString() {
-            return writer.toString();
-        }
-
-        public void onOutput(CharSequence output) {
-            writer.append(output);
-        }
-    }
-
-    private static class TerminalDetectorStub implements Spec<FileDescriptor> {
-        boolean stdoutIsTerminal;
-        boolean stderrIsTerminal;
-
-        public boolean isSatisfiedBy(FileDescriptor element) {
-            if (element == FileDescriptor.out) {
-                return stdoutIsTerminal;
-            }
-            if (element == FileDescriptor.err) {
-                return stderrIsTerminal;
-            }
-            return false;
-        }
-
-        public TerminalDetectorStub stderrIsTerminal() {
-            stderrIsTerminal = true;
-            return this;
-        }
-
-        public TerminalDetectorStub stdoutIsTerminal() {
-            stdoutIsTerminal = true;
-            return this;
-        }
-    }
-
-    private static class ConsoleStub implements Console, TextArea {
-        private final StringWriter writer = new StringWriter();
-
-        @Override
-        public String toString() {
-            return writer.toString();
-        }
-
-        public Label addStatusBar() {
-            return new Label() {
-                public void setText(String text) {
-                }
-
-                public void close() {
-                }
-            };
-        }
-
-        public TextArea getMainArea() {
-            return this;
-        }
-
-        public void append(CharSequence text) {
-            writer.append(text);
-        }
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
new file mode 100644
index 0000000..6ff0322
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/AbstractStyledTextOutputTest.groovy
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.logging.StyledTextOutput.Style
+
+class AbstractStyledTextOutputTest extends OutputSpecification {
+    private final TestStyledTextOutput output = new TestStyledTextOutput()
+
+    def onOutputWritesText() {
+        when:
+        output.onOutput('some message')
+
+        then:
+        output.value == 'some message'
+    }
+
+    def writesNullText() {
+        when:
+        output.text(null)
+
+        then:
+        output.value == 'null'
+    }
+
+    def writesEndOfLine() {
+        when:
+        output.println()
+
+        then:
+        output.rawValue == System.getProperty('line.separator')
+    }
+
+    def appendsCharacter() {
+        when:
+        output.append('c' as char)
+
+        then:
+        output.value == 'c'
+    }
+
+    def appendsCharSequence() {
+        when:
+        output.append('some message')
+
+        then:
+        output.value == 'some message'
+    }
+
+    def appendsNullCharSequence() {
+        when:
+        output.append(null)
+
+        then:
+        output.value == 'null'
+    }
+
+    def appendsCharSubsequence() {
+        when:
+        output.append('some message', 5, 9)
+
+        then:
+        output.value == 'mess'
+    }
+
+    def appendsNullCharSubsequence() {
+        when:
+        output.append(null, 5, 9)
+
+        then:
+        output.value == 'null'
+    }
+
+    def printlnWritesTextAndEndOfLine() {
+        when:
+        output.println('message')
+
+        then:
+        output.value == 'message\n'
+    }
+
+    def formatsText() {
+        when:
+        output.format('[%s]', 'message')
+
+        then:
+        output.value == '[message]'
+    }
+
+    def formatsTextAndEndOfLine() {
+        when:
+        output.formatln('[%s]', 'message')
+
+        then:
+        output.value == '[message]\n'
+    }
+
+    def formatsException() {
+        when:
+        output.exception(new RuntimeException('broken'))
+
+        then:
+        output.value == 'java.lang.RuntimeException: broken\n{stacktrace}\n'
+    }
+
+    def canMixInStyleInformation() {
+        when:
+        output.style(Style.Info).text('info test').style(Style.Normal)
+
+        then:
+        output.value == '{info}info test{normal}'
+    }
+
+    def ignoresStyleChangeWhenAlreadyUsingTheGivenStyle() {
+        when:
+        output.style(Style.Info).text('info test').style(Style.Info)
+
+        then:
+        output.value == '{info}info test'
+    }
+
+    def writesTextWithTemporaryStyleChange() {
+        when:
+        output.style(Style.Info).withStyle(Style.Error).text('some text')
+
+        then:
+        output.value == '{info}{error}some text{info}'
+    }
+
+    def writesTextAndEndOfLineWithTemporaryStyleChange() {
+        when:
+        output.style(Style.Info).withStyle(Style.Error).println('some text')
+
+        then:
+        output.value == '{info}{error}some text{info}\n'
+    }
+
+    def appendsTextWithTemporaryStyleChange() {
+        when:
+        output.style(Style.Info).withStyle(Style.Error).append('some text')
+
+        then:
+        output.value == '{info}{error}some text{info}'
+    }
+
+    def appendsCharacterWithTemporaryStyleChange() {
+        when:
+        output.style(Style.Info).withStyle(Style.Error).append('c' as char)
+
+        then:
+        output.value == '{info}{error}c{info}'
+    }
+
+    def formatsTextWithTemporaryStyleChange() {
+        when:
+        output.style(Style.Info).withStyle(Style.Error).format('[%s]', 'message')
+
+        then:
+        output.value == '{info}{error}[message]{info}'
+    }
+}
+
+class TestStyledTextOutput extends AbstractStyledTextOutput {
+    StringBuilder result = new StringBuilder()
+
+    @Override
+    String toString() {
+        result.toString()
+    }
+
+    def TestStyledTextOutput ignoreStyle() {
+        return new TestStyledTextOutput() {
+            @Override protected void doStyleChange(Style style) {
+            }
+        }
+    }
+
+    def String getRawValue() {
+        return result.toString()
+    }
+
+    /**
+     * Returns the normalised value of this text output. Normalises:
+     * - style changes to {style} where _style_ is the lowercase name of the style.
+     * - line endings to \n
+     * - stack traces to {stacktrace}\n
+     */
+    def String getValue() {
+        StringBuilder normalised = new StringBuilder()
+
+        String eol = System.getProperty('line.separator')
+        boolean inStackTrace = false
+        new StringTokenizer(result.toString().replaceAll(eol, '\n'), '\n', true).each { String line ->
+            if (line == '\n') {
+                if (!inStackTrace) {
+                    normalised.append('\n')
+                }
+            } else if (line.matches(/\s+at .+\(.+\)/)) {
+                if (!inStackTrace) {
+                    normalised.append('{stacktrace}\n')
+                }
+                inStackTrace = true
+            } else {
+                inStackTrace = false
+                normalised.append(line)
+            }
+        }
+        return normalised.toString()
+    }
+
+    @Override protected void doStyleChange(Style style) {
+        result.append("{${style.toString().toLowerCase()}}")
+    }
+
+    @Override
+    protected void doAppend(String text) {
+        result.append(text)
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
new file mode 100644
index 0000000..e24efa4
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/AnsiConsoleTest.groovy
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.fusesource.jansi.Ansi
+import org.fusesource.jansi.Ansi.Color
+import org.gradle.logging.StyledTextOutput
+import org.gradle.logging.StyledTextOutput.Style
+import org.gradle.util.JUnit4GroovyMockery
+import org.jmock.integration.junit4.JMock
+import org.junit.Test
+import org.junit.runner.RunWith
+
+ at RunWith(JMock.class)
+class AnsiConsoleTest {
+    private static final String EOL = System.getProperty('line.separator')
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+    private final Ansi ansi = context.mock(Ansi.class)
+    private final Appendable target = {} as Appendable
+    private final Flushable flushable = {} as Flushable
+    private final TestColorMap colorMap = new TestColorMap()
+    private final AnsiConsole console = new AnsiConsole(target, flushable, colorMap) {
+        def Ansi createAnsi() {
+            return ansi
+        }
+    }
+
+    @Test
+    public void appendsTextToMainArea() {
+        context.checking {
+            one(ansi).a('message')
+        }
+
+        console.mainArea.append('message')
+
+        context.checking {
+            one(ansi).a('message2')
+            one(ansi).a(EOL)
+            one(ansi).a('message3')
+        }
+
+        console.mainArea.append("message2${EOL}message3")
+    }
+
+    @Test
+    public void appendsStyledTextToMainArea() {
+        context.checking {
+            one(ansi).fg(Color.YELLOW)
+            one(ansi).a('message')
+            one(ansi).fg(Color.DEFAULT)
+        }
+
+        console.mainArea.style(StyledTextOutput.Style.Header).append('message')
+    }
+
+    @Test
+    public void displaysStatusBarWithNonEmptyText() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text')
+        }
+
+        statusBar.text = 'text'
+    }
+
+    @Test
+    public void displaysStatusBarWhenTextInMainArea() {
+        context.checking {
+            one(ansi).a('message')
+            one(ansi).a(EOL)
+        }
+
+        console.mainArea.append("message${EOL}")
+
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text')
+        }
+
+        statusBar.text = 'text'
+    }
+
+    @Test
+    public void redrawsStatusBarWhenTextChangesValue() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('123')
+        }
+
+        statusBar.text = '123'
+
+        context.checking {
+            one(ansi).cursorLeft(3)
+            one(ansi).a('abc')
+        }
+
+        statusBar.text = 'abc'
+    }
+
+    @Test
+    public void redrawsStatusBarWhenTextChangesSuffix() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text 1')
+        }
+
+        statusBar.text = 'text 1'
+
+        context.checking {
+            one(ansi).cursorLeft(1)
+            one(ansi).a('2')
+        }
+
+        statusBar.text = 'text 2'
+    }
+
+    @Test
+    public void redrawsStatusBarWhenTextAdded() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text')
+        }
+
+        statusBar.text = 'text'
+
+        context.checking {
+            one(ansi).a(' 2')
+        }
+
+        statusBar.text = 'text 2'
+    }
+
+    @Test
+    public void redrawsStatusBarWhenTextRemoved() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text 1')
+        }
+
+        statusBar.text = 'text 1'
+
+        context.checking {
+            one(ansi).cursorLeft(3)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+        }
+
+        statusBar.text = 'tex'
+    }
+
+    @Test
+    public void redrawsStatusBarWhenTextSetToEmpty() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text')
+        }
+
+        statusBar.text = 'text'
+
+        context.checking {
+            one(ansi).cursorLeft(4)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+        }
+
+        statusBar.text = ''
+    }
+
+    @Test
+    public void drawsStatusBarWhenTextHasSomeAttribute() {
+        colorMap.statusBarOn = Ansi.Attribute.INTENSITY_BOLD
+        colorMap.statusBarOff = Ansi.Attribute.INTENSITY_BOLD_OFF
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a(Ansi.Attribute.INTENSITY_BOLD)
+            one(ansi).a('text')
+            one(ansi).a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+        }
+
+        statusBar.text = 'text'
+    }
+
+    @Test
+    public void removesStatusBarWhenClosed() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('text')
+        }
+
+        statusBar.text = 'text'
+
+        context.checking {
+            one(ansi).cursorLeft(4)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+        }
+
+        statusBar.close();
+    }
+
+    @Test
+    public void removesStatusBarWhenClosedAndThereIsTextInMainArea() {
+        def statusBar = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('some message')
+            one(ansi).newline()
+            one(ansi).a('text')
+        }
+
+        console.mainArea.append('some message')
+        statusBar.text = 'text'
+
+        context.checking {
+            one(ansi).cursorLeft(4)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+            one(ansi).cursorUp(1)
+            one(ansi).cursorRight(12)
+        }
+
+        statusBar.close();
+    }
+
+    @Test
+    public void canRedisplayStatusBarAfterItIsClosed() {
+        context.checking {
+            one(ansi).a('first')
+        }
+
+        console.getStatusBar().text = 'first'
+
+        context.checking {
+            one(ansi).cursorLeft(5)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+        }
+
+        console.getStatusBar().close()
+
+        Label second = console.getStatusBar()
+
+        context.checking {
+            one(ansi).a('second')
+        }
+
+        second.text = 'second'
+    }
+
+    @Test
+    public void appendsTextWhenStatusBarIsPresent() {
+        context.checking {
+            one(ansi).a('status')
+        }
+
+        console.getStatusBar().text = 'status'
+
+        context.checking {
+            one(ansi).cursorLeft(6)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+            one(ansi).a('message')
+            one(ansi).a(EOL)
+            one(ansi).a('status')
+        }
+
+        console.mainArea.append("message$EOL");
+    }
+
+    @Test
+    public void appendsTextWithNoEOLWhenStatusBarIsPresent() {
+        context.checking {
+            one(ansi).a('status')
+        }
+
+        console.getStatusBar().text = 'status'
+
+        context.checking {
+            one(ansi).cursorLeft(6)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+            one(ansi).a('message')
+            one(ansi).newline()
+            one(ansi).a('status')
+        }
+
+        console.mainArea.append('message');
+
+        context.checking {
+            one(ansi).cursorLeft(6)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+            one(ansi).cursorUp(1)
+            one(ansi).cursorRight(7)
+            one(ansi).a('message2')
+            one(ansi).newline()
+            one(ansi).a('status')
+        }
+
+        console.mainArea.append('message2');
+    }
+
+    @Test
+    public void addsStatusBarWhenNoTrailingEOLInMainArea() {
+        context.checking {
+            one(ansi).a('message')
+        }
+
+        console.mainArea.append('message')
+
+        context.checking {
+            one(ansi).newline()
+            one(ansi).a('status')
+        }
+
+        console.getStatusBar().text = 'status'
+
+        context.checking {
+            one(ansi).cursorLeft(6)
+            one(ansi).eraseLine(Ansi.Erase.FORWARD)
+            one(ansi).cursorUp(1)
+            one(ansi).cursorRight(7)
+            one(ansi).a('message2')
+            one(ansi).a(EOL)
+            one(ansi).a('status')
+        }
+
+        console.mainArea.append("message2${EOL}")
+    }
+
+}
+
+class TestColorMap implements ColorMap {
+    def Ansi.Attribute statusBarOn = Ansi.Attribute.RESET
+    def Ansi.Attribute statusBarOff = Ansi.Attribute.RESET
+
+    ColorMap.Color getStatusBarColor() {
+        if (statusBarOn == Ansi.Attribute.RESET) {
+            return {} as ColorMap.Color
+        }
+        return [on: {ansi -> ansi.a(statusBarOn) },
+                off: {ansi -> ansi.a(statusBarOff) }
+        ] as ColorMap.Color
+    }
+
+    ColorMap.Color getColourFor(Style style) {
+        if (style != StyledTextOutput.Style.Header) {
+            return {} as ColorMap.Color
+        }
+        return [on: {ansi -> ansi.fg(Ansi.Color.YELLOW) },
+                off: {ansi -> ansi.fg(Ansi.Color.DEFAULT) }
+        ] as ColorMap.Color
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
new file mode 100644
index 0000000..b97ed50
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ConsoleBackedProgressRendererTest.groovy
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+class ConsoleBackedProgressRendererTest extends OutputSpecification {
+    private final OutputEventListener listener = Mock()
+    private final Console console = Mock()
+    private final Label statusBar = Mock()
+    private final ConsoleBackedProgressRenderer renderer = new ConsoleBackedProgressRenderer(listener, console)
+
+    def setup() {
+        (0..1) * console.getStatusBar() >> statusBar
+    }
+
+    def forwardsEventsToListener() {
+        def event = event('message')
+
+        when:
+        renderer.onOutput(event)
+
+        then:
+        1 * listener.onOutput(event)
+        0 * listener._
+        0 * statusBar._
+    }
+
+    def statusBarTracksOperationProgress() {
+        when:
+        renderer.onOutput(start('description'))
+
+        then:
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(progress('progress'))
+
+        then:
+        1 * statusBar.setText('> progress')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+    }
+
+    def statusBarTracksNestedOperationProgress() {
+        when:
+        renderer.onOutput(start('description'))
+
+        then:
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(progress('progress'))
+
+        then:
+        1 * statusBar.setText('> progress')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(start('description2'))
+
+        then:
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(progress('progress2'))
+
+        then:
+        1 * statusBar.setText('> progress > progress2')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('> progress')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+    }
+
+    def statusBarTracksNestedOperationProgressForOperationsWithNoStatus() {
+        when:
+        renderer.onOutput(start('description'))
+        renderer.onOutput(start('description2'))
+
+        then:
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(progress('progress'))
+
+        then:
+        1 * statusBar.setText('> progress')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+
+        when:
+        renderer.onOutput(complete('complete'))
+
+        then:
+        1 * statusBar.setText('')
+        0 * statusBar._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ConsoleStub.java b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ConsoleStub.java
new file mode 100644
index 0000000..b7083f0
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ConsoleStub.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+class ConsoleStub implements Console {
+    private final TextAreaImpl mainArea = new TextAreaImpl();
+
+    public Label getStatusBar() {
+        return new Label() {
+            public void close() {
+            }
+
+            public void setText(String text) {
+            }
+        };
+    }
+
+    public String getValue() {
+        return mainArea.toString();
+    }
+
+    public TextArea getMainArea() {
+        return mainArea;
+    }
+
+    private static class TextAreaImpl extends TestStyledTextOutput implements TextArea {
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultColorMapTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultColorMapTest.groovy
new file mode 100644
index 0000000..8839217
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultColorMapTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import org.junit.Rule
+import org.gradle.util.SetSystemProperties
+import org.gradle.logging.StyledTextOutput.Style
+import org.fusesource.jansi.Ansi
+import org.fusesource.jansi.Ansi.Color
+import org.fusesource.jansi.Ansi.Attribute
+
+class DefaultColorMapTest extends Specification {
+    @Rule public final SetSystemProperties sysProps = new SetSystemProperties()
+    private final DefaultColorMap map = new DefaultColorMap()
+
+    def canSetColorForAStyleUsingSystemProperty() {
+        System.properties['org.gradle.color.info'] = 'green'
+        Ansi ansi = Mock()
+
+        when:
+        def color = map.getColourFor(Style.Info)
+        color.on(ansi)
+
+        then:
+        1 * ansi.fg(Color.GREEN)
+        0 * ansi._
+
+        when:
+        color.off(ansi)
+
+        then:
+        1 * ansi.fg(Color.DEFAULT)
+        0 * ansi._
+    }
+
+    def canDisableColorForAStyleUsingSystemProperty() {
+        System.properties['org.gradle.color.info'] = 'default'
+        Ansi ansi = Mock()
+
+        when:
+        def color = map.getColourFor(Style.Info)
+        color.on(ansi)
+
+        then:
+        0 * ansi._
+
+        when:
+        color.off(ansi)
+
+        then:
+        0 * ansi._
+    }
+
+    def canSetBoldAttributeForAStyleUsingSystemProperty() {
+        System.properties['org.gradle.color.info'] = 'bold'
+        Ansi ansi = Mock()
+
+        when:
+        def color = map.getColourFor(Style.Info)
+        color.on(ansi)
+
+        then:
+        1 * ansi.a(Attribute.INTENSITY_BOLD)
+        0 * ansi._
+
+        when:
+        color.off(ansi)
+
+        then:
+        1 * ansi.a(Attribute.INTENSITY_BOLD_OFF)
+        0 * ansi._
+    }
+
+    def statusBarIsBoldByDefault() {
+        Ansi ansi = Mock()
+
+        when:
+        def color = map.statusBarColor
+        color.on(ansi)
+
+        then:
+        1 * ansi.a(Attribute.INTENSITY_BOLD)
+        0 * ansi._
+
+        when:
+        color.off(ansi)
+
+        then:
+        1 * ansi.a(Attribute.INTENSITY_BOLD_OFF)
+        0 * ansi._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java
new file mode 100644
index 0000000..b3dea4b
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultLoggingManagerTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal;
+
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.util.JUnit4GroovyMockery;
+import org.gradle.util.RedirectStdOutAndErr;
+import org.jmock.Expectations;
+import org.jmock.integration.junit4.JMock;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Hans Dockter
+ */
+ at RunWith(JMock.class)
+public class DefaultLoggingManagerTest {
+    @Rule
+    public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr();
+    private final JUnit4GroovyMockery context = new JUnit4GroovyMockery();
+    private final LoggingSystem loggingSystem = context.mock(LoggingSystem.class);
+    private final LoggingSystem stdOutLoggingSystem = context.mock(LoggingSystem.class);
+    private final LoggingSystem stdErrLoggingSystem = context.mock(LoggingSystem.class);
+    private final LoggingOutputInternal loggingOutput = context.mock(LoggingOutputInternal.class);
+    private final DefaultLoggingManager loggingManager = new DefaultLoggingManager(loggingSystem, stdOutLoggingSystem, stdErrLoggingSystem, loggingOutput);
+
+    @Test
+    public void defaultValues() {
+        assertTrue(loggingManager.isStandardOutputCaptureEnabled());
+        assertEquals(LogLevel.QUIET, loggingManager.getStandardOutputCaptureLevel());
+        assertEquals(LogLevel.ERROR, loggingManager.getStandardErrorCaptureLevel());
+        assertNull(loggingManager.getLevel());
+    }
+
+    @Test
+    public void canChangeStdOutCaptureLogLevel() {
+        loggingManager.captureStandardOutput(LogLevel.ERROR);
+        assertTrue(loggingManager.isStandardOutputCaptureEnabled());
+        assertEquals(LogLevel.ERROR, loggingManager.getStandardOutputCaptureLevel());
+    }
+
+    @Test
+    public void canChangeStdErrCaptureLogLevel() {
+        loggingManager.captureStandardError(LogLevel.WARN);
+        assertEquals(LogLevel.WARN, loggingManager.getStandardErrorCaptureLevel());
+    }
+
+    @Test
+    public void canChangeLogLevel() {
+        loggingManager.setLevel(LogLevel.ERROR);
+        assertEquals(LogLevel.ERROR, loggingManager.getLevel());
+    }
+
+    @Test
+    public void canDisableCapture() {
+        loggingManager.disableStandardOutputCapture();
+        assertFalse(loggingManager.isStandardOutputCaptureEnabled());
+        assertNull(loggingManager.getStandardOutputCaptureLevel());
+    }
+
+    @Test
+    public void startStopWithCaptureDisabled() {
+        loggingManager.disableStandardOutputCapture();
+
+        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            one(stdOutLoggingSystem).off();
+            will(returnValue(stdOutSnapshot));
+            one(stdErrLoggingSystem).off();
+            will(returnValue(stdErrSnapshot));
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).restore(stdOutSnapshot);
+            one(stdErrLoggingSystem).restore(stdErrSnapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void startStopWithCaptureEnabled() {
+        loggingManager.captureStandardOutput(LogLevel.DEBUG);
+        loggingManager.captureStandardError(LogLevel.INFO);
+
+        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
+            will(returnValue(stdOutSnapshot));
+            one(stdErrLoggingSystem).on(LogLevel.INFO);
+            will(returnValue(stdErrSnapshot));
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).restore(stdOutSnapshot);
+            one(stdErrLoggingSystem).restore(stdErrSnapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void startStopWithLogLevelSet() {
+        loggingManager.setLevel(LogLevel.DEBUG);
+
+        final LoggingSystem.Snapshot snapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingSystem).on(LogLevel.DEBUG);
+            will(returnValue(snapshot));
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingSystem).restore(snapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void startStopWithLogLevelNotSet() {
+        final LoggingSystem.Snapshot snapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingSystem).snapshot();
+            will(returnValue(snapshot));
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingSystem).restore(snapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void disableCaptureWhileStarted() {
+        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
+            will(returnValue(stdOutSnapshot));
+            one(stdErrLoggingSystem).on(LogLevel.INFO);
+            will(returnValue(stdErrSnapshot));
+        }});
+
+        loggingManager.captureStandardOutput(LogLevel.DEBUG);
+        loggingManager.captureStandardError(LogLevel.INFO);
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).off();
+            one(stdErrLoggingSystem).off();
+        }});
+
+        loggingManager.disableStandardOutputCapture();
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).restore(stdOutSnapshot);
+            one(stdErrLoggingSystem).restore(stdErrSnapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void enableCaptureWhileStarted() {
+        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            one(stdOutLoggingSystem).off();
+            will(returnValue(stdOutSnapshot));
+            one(stdErrLoggingSystem).off();
+            will(returnValue(stdErrSnapshot));
+        }});
+
+        loggingManager.disableStandardOutputCapture();
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
+            one(stdErrLoggingSystem).on(LogLevel.INFO);
+        }});
+
+        loggingManager.captureStandardOutput(LogLevel.DEBUG);
+        loggingManager.captureStandardError(LogLevel.INFO);
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).restore(stdOutSnapshot);
+            one(stdErrLoggingSystem).restore(stdErrSnapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void changeCaptureLevelWhileStarted() {
+        final LoggingSystem.Snapshot stdOutSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        final LoggingSystem.Snapshot stdErrSnapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            one(stdOutLoggingSystem).on(LogLevel.DEBUG);
+            will(returnValue(stdOutSnapshot));
+            one(stdErrLoggingSystem).on(LogLevel.DEBUG);
+            will(returnValue(stdErrSnapshot));
+        }});
+
+        loggingManager.captureStandardOutput(LogLevel.DEBUG);
+        loggingManager.captureStandardError(LogLevel.DEBUG);
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).on(LogLevel.WARN);
+        }});
+
+        loggingManager.captureStandardOutput(LogLevel.WARN);
+
+        context.checking(new Expectations() {{
+            one(stdOutLoggingSystem).restore(stdOutSnapshot);
+            one(stdErrLoggingSystem).restore(stdErrSnapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void changeLogLevelWhileStarted() {
+        final LoggingSystem.Snapshot snapshot = context.mock(LoggingSystem.Snapshot.class);
+        context.checking(new Expectations() {{
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingSystem).snapshot();
+            will(returnValue(snapshot));
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            ignoring(stdOutLoggingSystem);
+            one(loggingSystem).on(LogLevel.LIFECYCLE);
+            will(returnValue(context.mock(LoggingSystem.Snapshot.class)));
+        }});
+
+        loggingManager.setLevel(LogLevel.LIFECYCLE);
+
+        context.checking(new Expectations() {{
+            one(loggingSystem).restore(snapshot);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void addsListenersOnStartAndRemovesOnStop() {
+        final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
+        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
+
+        loggingManager.addStandardOutputListener(stdoutListener);
+        loggingManager.addStandardErrorListener(stderrListener);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingOutput).addStandardOutputListener(stdoutListener);
+            one(loggingOutput).addStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeStandardOutputListener(stdoutListener);
+            one(loggingOutput).removeStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.stop();
+    }
+
+    @Test
+    public void addsListenersWhileStarted() {
+        final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
+        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).addStandardOutputListener(stdoutListener);
+            one(loggingOutput).addStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.addStandardOutputListener(stdoutListener);
+        loggingManager.addStandardErrorListener(stderrListener);
+    }
+
+    @Test
+    public void removesListenersWhileStarted() {
+        final StandardOutputListener stdoutListener = context.mock(StandardOutputListener.class);
+        final StandardOutputListener stderrListener = context.mock(StandardOutputListener.class);
+
+        loggingManager.addStandardOutputListener(stdoutListener);
+        loggingManager.addStandardErrorListener(stderrListener);
+
+        context.checking(new Expectations() {{
+            ignoring(loggingSystem);
+            ignoring(stdOutLoggingSystem);
+            ignoring(stdErrLoggingSystem);
+            one(loggingOutput).addStandardOutputListener(stdoutListener);
+            one(loggingOutput).addStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.start();
+
+        context.checking(new Expectations() {{
+            one(loggingOutput).removeStandardOutputListener(stdoutListener);
+            one(loggingOutput).removeStandardErrorListener(stderrListener);
+        }});
+
+        loggingManager.removeStandardOutputListener(stdoutListener);
+        loggingManager.removeStandardErrorListener(stderrListener);
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy
new file mode 100644
index 0000000..9d7e5b1
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultProgressLoggerFactoryTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import org.gradle.logging.ProgressLoggerFactory
+import org.gradle.util.TimeProvider
+
+class DefaultProgressLoggerFactoryTest extends Specification {
+    private final ProgressListener progressListener = Mock()
+    private final TimeProvider timeProvider = Mock()
+    private final ProgressLoggerFactory factory = new DefaultProgressLoggerFactory(progressListener, timeProvider)
+
+    def progressLoggerBroadcastsEvents() {
+        when:
+        def logger = factory.start('logger', 'description')
+
+        then:
+        logger != null
+        1 * timeProvider.getCurrentTime() >> 100L
+        1 * progressListener.started({it.timestamp == 100L && it.category == 'logger' && it.description == 'description'})
+
+        when:
+        logger.progress('progress')
+
+        then:
+        1 * timeProvider.getCurrentTime() >> 200L
+        1 * progressListener.progress({it.timestamp == 200L && it.category == 'logger' && it.status == 'progress'})
+
+        when:
+        logger.completed('completed')
+
+        then:
+        1 * timeProvider.getCurrentTime() >> 300L
+        1 * progressListener.completed({it.timestamp == 300L && it.category == 'logger' && it.status == 'completed'})
+    }
+
+    def hasEmptyStatusOnStart() {
+        when:
+        def logger = factory.start('logger', 'description')
+
+        then:
+        logger.description == 'description'
+        logger.status == ''
+    }
+
+    def hasMostRecentStatusOnProgress() {
+        when:
+        def logger = factory.start('logger', 'description')
+        logger.progress('status')
+
+        then:
+        logger.status == 'status'
+    }
+    
+    def hasMostRecentStatusOnComplete() {
+        when:
+        def logger = factory.start('logger', 'description')
+        logger.completed('done')
+
+        then:
+        logger.status == 'done'
+    }
+}
+
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
new file mode 100644
index 0000000..e927d69
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/DefaultStandardOutputRedirectorTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.api.logging.StandardOutputListener
+import org.gradle.util.RedirectStdOutAndErr
+import org.junit.Rule
+import spock.lang.Specification
+
+class DefaultStandardOutputRedirectorTest extends Specification {
+    private static final String EOL = System.getProperty('line.separator')
+    @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
+    private final DefaultStandardOutputRedirector redirector = new DefaultStandardOutputRedirector()
+    private final StandardOutputListener stdOutListener = Mock()
+    private final StandardOutputListener stdErrListener = Mock()
+
+    def startAndStopDoesNothingWhenNothingRedirected() {
+        when:
+        redirector.start()
+        System.out.println('this is stdout')
+        System.err.println('this is stderr')
+        redirector.stop()
+
+        then:
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
+    
+    def startAndStopRedirectsStdOut() {
+        when:
+        redirector.redirectStandardOutputTo(stdOutListener)
+        redirector.start()
+        System.out.println('this is stdout')
+        System.err.println('this is stderr')
+        redirector.stop()
+
+        then:
+        1 * stdOutListener.onOutput('this is stdout' + EOL)
+        0 * stdOutListener._
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
+
+    def startAndStopRedirectsStdErr() {
+        when:
+        redirector.redirectStandardErrorTo(stdErrListener)
+        redirector.start()
+        System.out.println('this is stdout')
+        System.err.println('this is stderr')
+        redirector.stop()
+
+        then:
+        1 * stdErrListener.onOutput('this is stderr' + EOL)
+        0 * stdErrListener._
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
+    
+    def canRedirectMultipleTimes() {
+        when:
+        redirector.redirectStandardErrorTo(stdErrListener)
+        redirector.start()
+        redirector.stop()
+        redirector.redirectStandardOutputTo(stdOutListener)
+        redirector.start()
+        System.out.println('this is stdout')
+        System.err.println('this is stderr')
+        redirector.stop()
+
+        then:
+        1 * stdOutListener.onOutput('this is stdout' + EOL)
+        0 * stdOutListener._
+        0 * stdErrListener._
+        System.out == outputs.stdOutPrintStream
+        System.err == outputs.stdErrPrintStream
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy
new file mode 100644
index 0000000..32887df
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/JavaUtilLoggingConfigurerTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import java.util.logging.LogManager
+import ch.qos.logback.classic.spi.ILoggingEvent
+import ch.qos.logback.core.Appender
+import org.gradle.api.logging.LogLevel
+import java.util.logging.Logger
+import ch.qos.logback.classic.Level
+import org.gradle.logging.LoggingTestHelper
+
+class JavaUtilLoggingConfigurerTest extends Specification {
+    private final Appender<ILoggingEvent> appender = Mock()
+    private final LoggingTestHelper helper = new LoggingTestHelper(appender)
+    private final JavaUtilLoggingConfigurer configurer = new JavaUtilLoggingConfigurer()
+
+    def setup() {
+        helper.attachAppender()
+    }
+
+    def cleanup() {
+        helper.detachAppender()
+        LogManager.getLogManager().reset()
+    }
+
+    def routesJulToSlf4j() {
+        when:
+        configurer.configure(LogLevel.DEBUG)
+        Logger.getLogger('test').info('info message')
+
+        then:
+        1 * appender.doAppend({ILoggingEvent event -> event.level == Level.INFO && event.message == 'info message'})
+        0 * appender._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LogEventTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LogEventTest.groovy
new file mode 100644
index 0000000..e00db0c
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LogEventTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import org.gradle.api.logging.LogLevel
+import org.gradle.logging.StyledTextOutput
+
+class LogEventTest extends Specification {
+    private final StyledTextOutput output = Mock()
+
+    def renderWritesMessageToTextOutput() {
+        when:
+        new LogEvent(0, 'category', LogLevel.INFO, 'message', null).render(output)
+
+        then:
+        1 * output.text('message')
+        1 * output.println()
+        0 * output._
+    }
+    
+    def renderWritesMessageAndExceptionToTextOutput() {
+        def failure = new RuntimeException()
+
+        when:
+        new LogEvent(0, 'category', LogLevel.INFO, 'message', failure).render(output)
+
+        then:
+        1 * output.text('message')
+        1 * output.println()
+        1 * output.exception(failure)
+        0 * output._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingBackedStyledTextOutputTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingBackedStyledTextOutputTest.groovy
new file mode 100644
index 0000000..225d71a
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingBackedStyledTextOutputTest.groovy
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.util.TimeProvider
+import static org.gradle.logging.StyledTextOutput.Style.*
+import org.gradle.api.logging.LogLevel
+
+class LoggingBackedStyledTextOutputTest extends OutputSpecification {
+    private final OutputEventListener listener = Mock()
+    private final TimeProvider timeProvider = { 1200L } as TimeProvider
+    private final LoggingBackedStyledTextOutput output = new LoggingBackedStyledTextOutput(listener, 'category', LogLevel.INFO, timeProvider)
+
+    def forwardsLineOfTextToListener() {
+        when:
+        output.println('message')
+
+        then:
+        1 * listener.onOutput({it.spans[0].text == toNative('message\n')})
+        0 * listener._
+    }
+
+    def fillsInDetailsOfEvent() {
+        when:
+        output.text('message').println()
+
+        then:
+        1 * listener.onOutput(!null) >> { args ->
+            def event = args[0]
+            assert event.spans[0].style == Normal
+            assert event.category == 'category'
+            assert event.logLevel == LogLevel.INFO
+            assert event.timestamp == 1200
+            assert event.spans[0].text == toNative('message\n')
+        }
+        0 * listener._
+    }
+
+    def buffersTextUntilEndOfLineReached() {
+        when:
+        output.text('message ').text(toNative('with more\nanother ')).text('line').println()
+
+        then:
+        1 * listener.onOutput({it.spans[0].text == toNative('message with more\n')})
+        1 * listener.onOutput({it.spans[0].text == toNative('another line\n')})
+        0 * listener._
+    }
+    
+    def forwardsEachLineOfTextToListener() {
+        when:
+        output.text(toNative('message1\nmessage2')).println()
+
+        then:
+        1 * listener.onOutput({it.spans[0].text == toNative('message1\n')})
+        1 * listener.onOutput({it.spans[0].text == toNative('message2\n')})
+        0 * listener._
+    }
+
+    def forwardsEmptyLinesToListener() {
+        when:
+        output.text(toNative('\n\n'))
+
+        then:
+        2 * listener.onOutput({it.spans[0].text == toNative('\n')})
+        0 * listener._
+    }
+
+    def canChangeTheStyle() {
+        when:
+        output.style(Header)
+        output.println('message')
+
+        then:
+        1 * listener.onOutput(!null) >> { args ->
+            def event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].style == Header
+            assert event.spans[0].text == toNative('message\n')
+        }
+        0 * listener._
+    }
+    
+    def canChangeTheStyleInsideALine() {
+        when:
+        output.style(Header)
+        output.text('header')
+        output.style(Normal)
+        output.text('normal')
+        output.println()
+
+        then:
+        1 * listener.onOutput(!null) >> { args ->
+            def event = args[0]
+            assert event.spans.size() == 2
+            assert event.spans[0].style == Header
+            assert event.spans[0].text == 'header'
+            assert event.spans[1].style == Normal
+            assert event.spans[1].text == toNative('normal\n')
+        }
+        0 * listener._
+    }
+
+    def ignoresEmptySpans() {
+        when:
+        output.style(Header)
+        output.text('')
+        output.style(Normal)
+        output.style(UserInput)
+        output.println()
+
+        then:
+        1 * listener.onOutput(!null) >> { args ->
+            def event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].style == UserInput
+            assert event.spans[0].text == toNative('\n')
+        }
+        0 * listener._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy
new file mode 100644
index 0000000..5a49b29
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingCommandLineConverterTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.logging.LoggingConfiguration
+import spock.lang.Specification
+
+class LoggingCommandLineConverterTest extends Specification {
+    final LoggingCommandLineConverter converter = new LoggingCommandLineConverter()
+    final LoggingConfiguration expectedConfig = new LoggingConfiguration()
+
+    def convertsEmptyArgs() {
+        expect:
+        checkConversion([])
+    }
+
+    def convertsDebugLevel() {
+        expectedConfig.logLevel = LogLevel.DEBUG
+
+        expect:
+        checkConversion(['-d'])
+        checkConversion(['--debug'])
+    }
+
+    def convertsInfoLevel() {
+        expectedConfig.logLevel = LogLevel.INFO
+
+        expect:
+        checkConversion(['-i'])
+        checkConversion(['--info'])
+    }
+
+    def convertsQuietLevel() {
+        expectedConfig.logLevel = LogLevel.QUIET
+
+        expect:
+        checkConversion(['-q'])
+        checkConversion(['--quiet'])
+    }
+
+    def convertsNoColor() {
+        expectedConfig.colorOutput = false
+
+        expect:
+        checkConversion(['--no-color'])
+    }
+
+    void checkConversion(List<String> args) {
+        def actual = converter.convert(args)
+        assert actual.logLevel == expectedConfig.logLevel
+        assert actual.colorOutput == expectedConfig.colorOutput
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingSystemAdapterTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingSystemAdapterTest.groovy
new file mode 100644
index 0000000..643ba41
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/LoggingSystemAdapterTest.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+
+import org.gradle.api.logging.LogLevel
+
+class LoggingSystemAdapterTest extends Specification {
+    private final LoggingConfigurer loggingConfigurer = Mock()
+    private final LoggingSystemAdapter loggingSystem = new LoggingSystemAdapter(loggingConfigurer)
+
+    def onUsesLoggingConfigurerToSetLoggingLevel() {
+        when:
+        loggingSystem.on(LogLevel.DEBUG)
+
+        then:
+        1 * loggingConfigurer.configure(LogLevel.DEBUG)
+        0 * loggingConfigurer._
+    }
+
+    def offDoesNothing() {
+        when:
+        loggingSystem.off()
+
+        then:
+        0 * loggingConfigurer._
+    }
+
+    def restoreSetsLoggingLevelToPreviousLoggingLevel() {
+        when:
+        def snapshot = loggingSystem.snapshot()
+        loggingSystem.restore(snapshot)
+
+        then:
+        1 * loggingConfigurer.configure(LogLevel.LIFECYCLE)
+    }
+    
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
new file mode 100644
index 0000000..f28c48c
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/OutputEventRendererTest.groovy
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.api.logging.StandardOutputListener
+import org.gradle.util.RedirectStdOutAndErr
+import org.junit.Rule
+
+class OutputEventRendererTest extends OutputSpecification {
+    @Rule public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
+    private final ConsoleStub console = new ConsoleStub()
+    private OutputEventRenderer renderer
+
+    def setup() {
+        renderer = new OutputEventRenderer()
+        renderer.addStandardOutput(outputs.stdOutPrintStream)
+        renderer.addStandardError(outputs.stdErrPrintStream)
+        renderer.configure(LogLevel.INFO)
+    }
+
+    def rendersLogEventsToStdOut() {
+        when:
+        renderer.onOutput(event('message', LogLevel.INFO))
+
+        then:
+        outputs.stdOut.readLines() == ['message']
+        outputs.stdErr == ''
+    }
+
+    def rendersErrorLogEventsToStdErr() {
+        when:
+        renderer.onOutput(event('message', LogLevel.ERROR))
+
+        then:
+        outputs.stdOut == ''
+        outputs.stdErr.readLines() == ['message']
+    }
+
+    def rendersLogEventsWhenLogLevelIsDebug() {
+        when:
+        renderer.configure(LogLevel.DEBUG)
+        renderer.onOutput(event(tenAm, 'message', LogLevel.INFO))
+
+        then:
+        outputs.stdOut.readLines() == ['10:00:00.000 [INFO] [category] message']
+        outputs.stdErr == ''
+    }
+
+    def rendersLogEventsToStdOutListener() {
+        def listener = new TestListener()
+
+        when:
+        renderer.addStandardOutputListener(listener)
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+
+        then:
+        listener.value.readLines() == ['info']
+    }
+
+    def doesNotRenderLogEventsToRemovedStdOutListener() {
+        def listener = new TestListener()
+
+        when:
+        renderer.addStandardOutputListener(listener)
+        renderer.removeStandardOutputListener(listener)
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+
+        then:
+        listener.value == ''
+    }
+
+    def rendersLogEventsToStdOutListenerWhenLogLevelIsDebug() {
+        def listener = new TestListener()
+
+        when:
+        renderer.configure(LogLevel.DEBUG)
+        renderer.addStandardOutputListener(listener)
+        renderer.onOutput(event(tenAm, 'message', LogLevel.INFO))
+
+        then:
+        listener.value.readLines() == ['10:00:00.000 [INFO] [category] message']
+    }
+    
+    def rendersErrorLogEventsToStdErrListener() {
+        def listener = new TestListener()
+
+        when:
+        renderer.addStandardErrorListener(listener)
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+
+        then:
+        listener.value.readLines() == ['error']
+    }
+
+    def doesNotRenderLogEventsToRemovedStdErrListener() {
+        def listener = new TestListener()
+
+        when:
+        renderer.addStandardErrorListener(listener)
+        renderer.removeStandardErrorListener(listener)
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+
+        then:
+        listener.value == ''
+    }
+
+    def rendersLogEventsToStdErrListenerWhenLogLevelIsDebug() {
+        def listener = new TestListener()
+
+        when:
+        renderer.configure(LogLevel.DEBUG)
+        renderer.addStandardErrorListener(listener)
+        renderer.onOutput(event(tenAm, 'message', LogLevel.ERROR))
+
+        then:
+        listener.value.readLines() == ['10:00:00.000 [ERROR] [category] message']
+    }
+
+    def forwardsOutputEventsToListener() {
+        OutputEventListener listener = Mock()
+        LogEvent ignored = event('ignored', LogLevel.DEBUG)
+        LogEvent event = event('message', LogLevel.INFO)
+
+        when:
+        renderer.configure(LogLevel.INFO)
+        renderer.addOutputEventListener(listener)
+        renderer.onOutput(ignored)
+        renderer.onOutput(event)
+
+        then:
+        1 * listener.onOutput(event)
+        0 * listener._
+    }
+
+    def doesNotForwardOutputEventsToRemovedListener() {
+        OutputEventListener listener = Mock()
+        LogEvent event = event('message', LogLevel.INFO)
+
+        when:
+        renderer.configure(LogLevel.INFO)
+        renderer.addOutputEventListener(listener)
+        renderer.removeOutputEventListener(listener)
+        renderer.onOutput(event)
+
+        then:
+        0 * listener._
+    }
+
+    def rendersProgressEvents() {
+        when:
+        renderer.onOutput(start('description'))
+        renderer.onOutput(complete('status'))
+
+        then:
+        outputs.stdOut.readLines() == ['description status']
+        outputs.stdErr == ''
+    }
+
+    def doesNotRendersProgressEventsForLogLevelQuiet() {
+        when:
+        renderer.configure(LogLevel.QUIET)
+        renderer.onOutput(start('description'))
+        renderer.onOutput(complete('status'))
+
+        then:
+        outputs.stdOut == ''
+        outputs.stdErr == ''
+    }
+
+    def rendersLogEventsWhenStdOutAndStdErrAreTerminal() {
+        renderer.addConsole(console, true, true)
+
+        when:
+        renderer.onOutput(start('description'))
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+        renderer.onOutput(complete('status'))
+
+        then:
+        console.value.readLines() == ['description', 'info', '{error}error', '{normal}description {progressstatus}status{normal}']
+    }
+
+    def rendersLogEventsWhenOnlyStdOutIsTerminal() {
+        renderer.addConsole(console, true, false)
+
+        when:
+        renderer.onOutput(start('description'))
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+        renderer.onOutput(complete('status'))
+
+        then:
+        console.value.readLines() == ['description', 'info', 'description {progressstatus}status{normal}']
+    }
+
+    def rendersLogEventsWhenOnlyStdErrIsTerminal() {
+        renderer.addConsole(console, false, true)
+
+        when:
+        renderer.onOutput(start('description'))
+        renderer.onOutput(event('info', LogLevel.INFO))
+        renderer.onOutput(event('error', LogLevel.ERROR))
+        renderer.onOutput(complete('status'))
+
+        then:
+        console.value.readLines() == ['{error}error', '{normal}']
+    }
+}
+
+private static class TestListener implements StandardOutputListener {
+    private final StringWriter writer = new StringWriter();
+
+    def getValue() {
+        return writer.toString()
+    }
+
+    public void onOutput(CharSequence output) {
+        writer.append(output);
+    }
+}
+
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy
new file mode 100644
index 0000000..96e04bc
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/OutputSpecification.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import org.gradle.api.logging.LogLevel
+import java.text.SimpleDateFormat
+
+class OutputSpecification extends Specification {
+    def String toNative(String value) {
+        return value.replaceAll('\n', System.getProperty('line.separator'))
+    }
+
+    /**
+     * Returns timestamp representing 10AM today in local time.
+     */
+    def long getTenAm() {
+        return getTime('10:00:00.000')
+    }
+
+    def long getTime(String time) {
+        String today = new SimpleDateFormat("yyyyMMdd").format(new Date())
+        return new SimpleDateFormat('yyyyMMdd HH:mm:ss.SSS').parse(today + ' ' + time).getTime()
+    }
+
+    def LogEvent event(String text) {
+        return new LogEvent(tenAm, 'category', LogLevel.INFO, text, null)
+    }
+
+    def LogEvent event(String text, LogLevel logLevel) {
+        return new LogEvent(tenAm, 'category', logLevel, text, null)
+    }
+
+    def LogEvent event(long timestamp, String text, LogLevel logLevel) {
+        return new LogEvent(timestamp, 'category', logLevel, text, null)
+    }
+
+    def LogEvent event(long timestamp, String text) {
+        return new LogEvent(timestamp, 'category', LogLevel.INFO, text, null)
+    }
+
+    def LogEvent event(String text, Throwable throwable) {
+        return new LogEvent(tenAm, 'category', LogLevel.INFO, text, throwable)
+    }
+
+    def ProgressStartEvent start(String description) {
+        return new ProgressStartEvent(tenAm, 'category', description)
+    }
+
+    def ProgressEvent progress(String status) {
+        return new ProgressEvent(tenAm, 'category', status)
+    }
+
+    def ProgressCompleteEvent complete(String status) {
+        return new ProgressCompleteEvent(tenAm, 'category', status)
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/PrintStreamLoggingSystemTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/PrintStreamLoggingSystemTest.groovy
new file mode 100644
index 0000000..6ccf40d
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/PrintStreamLoggingSystemTest.groovy
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.logging.internal
+
+import org.gradle.api.logging.LogLevel
+import spock.lang.Specification
+import org.gradle.util.TimeProvider
+
+class PrintStreamLoggingSystemTest extends Specification {
+    private final OutputStream original = new ByteArrayOutputStream()
+    private PrintStream stream = new PrintStream(original)
+    private final OutputEventListener listener = Mock()
+    private final TimeProvider timeProvider = { 1200L } as TimeProvider
+    private final PrintStreamLoggingSystem loggingSystem = new PrintStreamLoggingSystem(listener, 'category', timeProvider) {
+        protected PrintStream get() {
+            stream
+        }
+
+        protected void set(PrintStream printStream) {
+            stream = printStream
+        }
+    }
+
+    def onStartsCapturingWhenNotAlreadyCapturing() {
+        when:
+        loggingSystem.on(LogLevel.INFO)
+        stream.println('info')
+
+        then:
+        1 * listener.onOutput({it instanceof LogLevelChangeEvent && it.newLogLevel == LogLevel.INFO})
+        1 * listener.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == withEOL('info')})
+        original.toString() == ''
+        0 * listener._
+    }
+
+    def fillsInEventDetails() {
+        when:
+        loggingSystem.on(LogLevel.INFO)
+        stream.println('info')
+
+        then:
+        1 * listener.onOutput({it instanceof StyledTextOutputEvent && it.category == 'category' && it.timestamp == 1200 && it.spans[0].text == withEOL('info')})
+    }
+
+    def onChangesLogLevelsWhenAlreadyCapturing() {
+        loggingSystem.on(LogLevel.INFO)
+
+        when:
+        loggingSystem.on(LogLevel.DEBUG)
+        stream.println('info')
+
+        then:
+        1 * listener.onOutput({it instanceof LogLevelChangeEvent && it.newLogLevel == LogLevel.DEBUG})
+        1 * listener.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == withEOL('info')})
+        original.toString() == ''
+        0 * listener._
+    }
+
+    def offDoesNothingWhenNotAlreadyCapturing() {
+        when:
+        loggingSystem.off()
+        stream.println('info')
+
+        then:
+        original.toString() == withEOL('info')
+        0 * listener._
+    }
+
+    def offStopsCapturingWhenAlreadyCapturing() {
+        loggingSystem.on(LogLevel.WARN)
+
+        when:
+        loggingSystem.off()
+
+        stream.println('info')
+
+        then:
+        original.toString() == withEOL('info')
+        0 * listener._
+    }
+
+    def restoreStopsCapturingWhenCapturingWasNotInstalledWhenSnapshotTaken() {
+        def snapshot = loggingSystem.snapshot()
+        loggingSystem.on(LogLevel.ERROR)
+
+        when:
+        loggingSystem.restore(snapshot)
+        stream.println('info')
+
+        then:
+        original.toString() == withEOL('info')
+        0 * listener._
+    }
+
+    def restoreStopsCapturingWhenCapturingWasOffWhenSnapshotTaken() {
+        loggingSystem.on(LogLevel.INFO)
+        loggingSystem.off()
+        def snapshot = loggingSystem.snapshot()
+        loggingSystem.on(LogLevel.ERROR)
+
+        when:
+        loggingSystem.restore(snapshot)
+        stream.println('info')
+
+        then:
+        original.toString() == withEOL('info')
+        0 * listener._
+    }
+
+    def restoreStartsCapturingWhenCapturingWasOnWhenSnapshotTaken() {
+        loggingSystem.on(LogLevel.WARN)
+        def snapshot = loggingSystem.snapshot()
+        loggingSystem.off()
+
+        when:
+        loggingSystem.restore(snapshot)
+        stream.println('info')
+
+        then:
+        1 * listener.onOutput({it instanceof LogLevelChangeEvent && it.newLogLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.spans[0].text == withEOL('info')})
+        original.toString() == ''
+        0 * listener._
+    }
+    
+    def restoreSetsLogLevelToTheLevelWhenSnapshotTaken() {
+        loggingSystem.on(LogLevel.WARN)
+        def snapshot = loggingSystem.snapshot()
+        loggingSystem.on(LogLevel.INFO)
+
+        when:
+        loggingSystem.restore(snapshot)
+        stream.println('info')
+
+        then:
+        1 * listener.onOutput({it instanceof LogLevelChangeEvent && it.newLogLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.spans[0].text == withEOL('info')})
+        original.toString() == ''
+        0 * listener._
+    }
+
+    private String withEOL(String value) {
+        return String.format('%s%n', value)
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy
new file mode 100644
index 0000000..8a10801
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/ProgressLogEventGeneratorTest.groovy
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+class ProgressLogEventGeneratorTest extends OutputSpecification {
+    private final OutputEventListener target = Mock()
+
+    def insertsLogMessagesWhenOperationStartsAndEnds() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+        def startEvent = start('description')
+        def completeEvent = complete('status')
+
+        when:
+        generator.onOutput(startEvent)
+
+        then:
+        1 * target.onOutput(!null) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans[0].text == 'description'
+            assert event.timestamp == startEvent.timestamp
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(completeEvent)
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative(' ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+            assert event.timestamp == completeEvent.timestamp
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationStartsAndEndsAndDeferredHeaderMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+        def startEvent = start('description')
+        def completeEvent = complete('status')
+
+        when:
+        generator.onOutput(startEvent)
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(completeEvent)
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('description ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+            assert event.timestamp == completeEvent.timestamp
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationGeneratesSomeOutput() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+        def logEvent = event('message')
+
+        when:
+        generator.onOutput(start('description'))
+
+        then:
+        1 * target.onOutput(!null) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].text == 'description'
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(logEvent)
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent}) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].text == toNative('\n')
+        }
+        1 * target.onOutput(logEvent)
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('description ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationGeneratesSomeOutputAndInDeferredHeaderMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+        def logEvent = event('message')
+        def startEvent = start('description')
+        def completeEvent = complete('status')
+
+        when:
+        generator.onOutput(startEvent)
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(logEvent)
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent}) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].text == toNative('description\n')
+            assert event.timestamp == startEvent.timestamp
+        }
+        1 * target.onOutput(logEvent)
+        0 * target._
+
+        when:
+        generator.onOutput(completeEvent)
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('description ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationsAreNested() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+
+        when:
+        generator.onOutput(start('description'))
+
+        then:
+        1 * target.onOutput(!null) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].text == toNative('description')
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(start('description2'))
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('\n')}) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].text == toNative('\n')
+        }
+        1 * target.onOutput(!null) >> { args->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 1
+            assert event.spans[0].text == toNative('description2')
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status2'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative(' ')
+            assert event.spans[1].text == toNative('status2')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('description ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationsAreNestedAndInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+
+        when:
+        generator.onOutput(start('description'))
+        generator.onOutput(start('description2'))
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status2'))
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('description\n')})
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('description2 ')
+            assert event.spans[1].text == toNative('status2')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative('description ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoFinalStatus() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+
+        when:
+        generator.onOutput(start('description'))
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == 'description'})
+        0 * target._
+
+        when:
+        generator.onOutput(complete(''))
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('\n')})
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoFinalStatusAndInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+
+        when:
+        generator.onOutput(start('description'))
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(complete(''))
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('description\n')})
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoDescription() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+
+        when:
+        generator.onOutput(start(''))
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 2
+            assert event.spans[0].text == toNative('status')
+            assert event.spans[1].text == toNative('\n')
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoDescriptionWhenInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+
+        when:
+        generator.onOutput(start(''))
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 2
+            assert event.spans[0].text == toNative('status')
+            assert event.spans[1].text == toNative('\n')
+        }
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoDescriptionOrFinalStatus() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+
+        when:
+        generator.onOutput(start(''))
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(complete(''))
+
+        then:
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoFinalStatusAndGeneratesLogMessages() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+        def event = event('event')
+
+        when:
+        generator.onOutput(start('description'))
+        generator.onOutput(event)
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('description')})
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('\n')})
+        1 * target.onOutput(event)
+        0 * target._
+
+        when:
+        generator.onOutput(complete(''))
+
+        then:
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationHasNoFinalStatusAndGeneratesLogMessagesAndInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+        def event = event('event')
+
+        when:
+        generator.onOutput(start('description'))
+        generator.onOutput(event)
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('description\n')})
+        1 * target.onOutput(event)
+        0 * target._
+
+        when:
+        generator.onOutput(complete(''))
+
+        then:
+        0 * target._
+    }
+
+    def insertsLogMessagesWhenOperationsAreNestedAndHaveNoDescriptionAndInDeferredMode() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, true)
+
+        when:
+        generator.onOutput(start(''))
+        generator.onOutput(start(''))
+
+        then:
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status2'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 2
+            assert event.spans[0].text == toNative('status2')
+            assert event.spans[1].text == toNative('\n')
+        }
+        0 * target._
+
+        when:
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 2
+            assert event.spans[0].text == toNative('status')
+            assert event.spans[1].text == toNative('\n')
+        }
+        0 * target._
+    }
+    
+    def ignoresProgressEvents() {
+        ProgressLogEventGenerator generator = new ProgressLogEventGenerator(target, false)
+
+        when:
+        generator.onOutput(start('description'))
+        generator.onOutput(progress('tick'))
+        generator.onOutput(complete('status'))
+
+        then:
+        1 * target.onOutput({it instanceof StyledTextOutputEvent && it.spans[0].text == toNative('description')})
+        1 * target.onOutput(!null) >> { args ->
+            StyledTextOutputEvent event = args[0]
+            assert event.spans.size() == 3
+            assert event.spans[0].text == toNative(' ')
+            assert event.spans[1].text == toNative('status')
+            assert event.spans[2].text == toNative('\n')
+        }
+        0 * target._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/Slf4jLoggingConfigurerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/Slf4jLoggingConfigurerTest.groovy
new file mode 100644
index 0000000..f526a4a
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/Slf4jLoggingConfigurerTest.groovy
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import ch.qos.logback.classic.LoggerContext
+import org.slf4j.LoggerFactory
+
+import org.gradle.api.logging.LogLevel
+import org.slf4j.Logger
+import org.gradle.api.logging.Logging
+
+class Slf4jLoggingConfigurerTest extends Specification {
+    private final Logger logger = LoggerFactory.getLogger("cat1");
+    private final OutputEventListener listener = Mock()
+    private final Slf4jLoggingConfigurer configurer = new Slf4jLoggingConfigurer(listener)
+    
+    def cleanup() {
+        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        lc.reset();
+    }
+
+    def routesSlf4jLogEventsToOutputEventListener() {
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message')
+
+        then:
+        1 * listener.onOutput({it.category == 'cat1' && it.message == 'message' && it.logLevel == LogLevel.INFO && it.throwable == null})
+        0 * listener._
+    }
+
+    def includesThrowableInLogEvent() {
+        def failure = new RuntimeException()
+
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message', failure)
+
+        then:
+        1 * listener.onOutput({it.category == 'cat1' && it.message == 'message' && it.logLevel == LogLevel.INFO && it.throwable == failure})
+        0 * listener._
+    }
+    
+    def mapsSlf4jLogLevelsToGradleLogLevels() {
+        when:
+        configurer.configure(LogLevel.DEBUG)
+
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'debug' && it.logLevel == LogLevel.DEBUG})
+        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def formatsLogMessage() {
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message {} {}', 'arg1', 'arg2')
+
+        then:
+        1 * listener.onOutput({it.message == 'message arg1 arg2'})
+        0 * listener._
+    }
+
+    def attachesATimestamp() {
+        when:
+        configurer.configure(LogLevel.INFO)
+        logger.info('message')
+
+        then:
+        1 * listener.onOutput({it.timestamp >= System.currentTimeMillis() - 300})
+        0 * listener._
+    }
+
+    def filtersLifecycleAndLowerWhenConfiguredAtQuietLevel() {
+        when:
+        configurer.configure(LogLevel.QUIET)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def filtersInfoAndLowerWhenConfiguredAtLifecycleLevel() {
+        when:
+        configurer.configure(LogLevel.LIFECYCLE)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def filtersDebugAndLowerWhenConfiguredAtInfoLevel() {
+        when:
+        configurer.configure(LogLevel.INFO)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+
+    def filtersTraceWhenConfiguredAtDebugLevel() {
+        when:
+        configurer.configure(LogLevel.DEBUG)
+
+        logger.trace('trace')
+        logger.debug('debug')
+        logger.info('info')
+        logger.info(Logging.LIFECYCLE, 'lifecycle')
+        logger.info(Logging.QUIET, 'quiet')
+        logger.warn('warn')
+        logger.error('error')
+
+        then:
+        1 * listener.onOutput({it.message == 'debug' && it.logLevel == LogLevel.DEBUG})
+        1 * listener.onOutput({it.message == 'info' && it.logLevel == LogLevel.INFO})
+        1 * listener.onOutput({it.message == 'lifecycle' && it.logLevel == LogLevel.LIFECYCLE})
+        1 * listener.onOutput({it.message == 'quiet' && it.logLevel == LogLevel.QUIET})
+        1 * listener.onOutput({it.message == 'warn' && it.logLevel == LogLevel.WARN})
+        1 * listener.onOutput({it.message == 'error' && it.logLevel == LogLevel.ERROR})
+        0 * listener._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StreamingStyledTextOutputTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StreamingStyledTextOutputTest.groovy
new file mode 100644
index 0000000..e082281
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StreamingStyledTextOutputTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.api.logging.StandardOutputListener
+import org.gradle.logging.StyledTextOutput.Style
+
+class StreamingStyledTextOutputTest extends OutputSpecification {
+    def forwardsTextToAListener() {
+        StandardOutputListener listener = Mock()
+        StreamingStyledTextOutput output = new StreamingStyledTextOutput(listener)
+
+        when:
+        output.text('text')
+
+        then:
+        1 * listener.onOutput('text')
+    }
+
+    def forwardsTextToAnAppendable() {
+        Appendable appendable = Mock()
+        StreamingStyledTextOutput output = new StreamingStyledTextOutput(appendable)
+
+        when:
+        output.text('text')
+
+        then:
+        1 * appendable.append('text')
+    }
+
+    def ignoresStyleInformation() {
+        StandardOutputListener listener = Mock()
+        StreamingStyledTextOutput output = new StreamingStyledTextOutput(listener)
+
+        when:
+        output.style(Style.Error).text('text').style(Style.Normal)
+
+        then:
+        1 * listener.onOutput('text')
+    }
+
+    def closeDoesNothingWhenForwardingToANonCloseable() {
+        Appendable appendable = Mock()
+        StreamingStyledTextOutput output = new StreamingStyledTextOutput(appendable)
+
+        when:
+        output.close()
+
+        then:
+        0 * appendable._
+    }
+    
+    def closeClosesTargetWhenItImplementsCloseable() {
+        Writer writer = Mock()
+        StreamingStyledTextOutput output = new StreamingStyledTextOutput(writer)
+
+        when:
+        output.close()
+
+        then:
+        1 * writer.close()
+    }
+
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StyledTextOutputBackedRendererTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StyledTextOutputBackedRendererTest.groovy
new file mode 100644
index 0000000..043b3a9
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StyledTextOutputBackedRendererTest.groovy
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.logging.StyledTextOutput
+import org.gradle.api.logging.LogLevel
+
+class StyledTextOutputBackedRendererTest extends OutputSpecification {
+    def rendersOutputEvent() {
+        StyledTextOutput output = new TestStyledTextOutput()
+        StyledTextOutputBackedRenderer renderer = new StyledTextOutputBackedRenderer(output)
+        RenderableOutputEvent event = Mock()
+
+        when:
+        renderer.onOutput(event)
+
+        then:
+        1 * event.render(!null) >> { args -> args[0].text('text') }
+        output.value == 'text'
+    }
+
+    def rendersErrorOutputEvent() {
+        StyledTextOutput output = new TestStyledTextOutput()
+        StyledTextOutputBackedRenderer renderer = new StyledTextOutputBackedRenderer(output)
+        RenderableOutputEvent event = Mock()
+
+        when:
+        renderer.onOutput(event)
+
+        then:
+        1 * event.logLevel >> LogLevel.ERROR
+        1 * event.render(!null) >> { args -> args[0].text('text') }
+        output.value == '{error}text{normal}'
+    }
+
+    def rendersException() {
+        StyledTextOutput output = new TestStyledTextOutput()
+        StyledTextOutputBackedRenderer renderer = new StyledTextOutputBackedRenderer(output)
+        RenderableOutputEvent event = Mock()
+        def failure = new RuntimeException('broken')
+
+        when:
+        renderer.onOutput(event)
+
+        then:
+        1 * event.render(!null) >> { args -> args[0].exception(failure) }
+        output.value == 'java.lang.RuntimeException: broken\n{stacktrace}\n'
+    }
+
+    def rendersOutputEventWhenInDebugMode() {
+        StyledTextOutput output = new TestStyledTextOutput()
+        StyledTextOutputBackedRenderer renderer = new StyledTextOutputBackedRenderer(output)
+        RenderableOutputEvent event = event(tenAm, 'message')
+
+        when:
+        renderer.onOutput(new LogLevelChangeEvent(LogLevel.DEBUG))
+        renderer.onOutput(event)
+
+        then:
+        output.value == '10:00:00.000 [INFO] [category] message\n'
+    }
+    
+    def continuesLineWhenPreviousOutputEventDidNotEndWithEOL() {
+        TestStyledTextOutput output = new TestStyledTextOutput()
+        StyledTextOutputBackedRenderer renderer = new StyledTextOutputBackedRenderer(output)
+        RenderableOutputEvent event1 = new StyledTextOutputEvent(tenAm, 'category', LogLevel.INFO, 'message')
+        RenderableOutputEvent event2 = new StyledTextOutputEvent(tenAm, 'category', LogLevel.INFO, toNative('\n'))
+
+        when:
+        renderer.onOutput(new LogLevelChangeEvent(LogLevel.DEBUG))
+        renderer.onOutput(event1)
+        renderer.onOutput(event2)
+
+        then:
+        output.value == '10:00:00.000 [INFO] [category] message\n'
+    }
+
+    def addsEOLWhenPreviousOutputEventDidNotEndWithEOLAndHadDifferentCategory() {
+        TestStyledTextOutput output = new TestStyledTextOutput()
+        StyledTextOutputBackedRenderer renderer = new StyledTextOutputBackedRenderer(output)
+        RenderableOutputEvent event1 = new StyledTextOutputEvent(tenAm, 'category', LogLevel.INFO, 'message')
+        RenderableOutputEvent event2 = new StyledTextOutputEvent(tenAm, 'category2', LogLevel.INFO, 'message2')
+
+        when:
+        renderer.onOutput(new LogLevelChangeEvent(LogLevel.DEBUG))
+        renderer.onOutput(event1)
+        renderer.onOutput(event2)
+
+        then:
+        output.value == '10:00:00.000 [INFO] [category] message\n10:00:00.000 [INFO] [category2] message2'
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StyledTextOutputEventTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StyledTextOutputEventTest.groovy
new file mode 100644
index 0000000..268929f
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/StyledTextOutputEventTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.logging.StyledTextOutput
+import spock.lang.Specification
+
+class StyledTextOutputEventTest extends Specification {
+
+    def canSetLogLevel() {
+        def event = new StyledTextOutputEvent(100, 'category', 'message').withLogLevel(LogLevel.DEBUG)
+
+        expect:
+        event.logLevel == LogLevel.DEBUG
+    }
+    
+    def rendersToTextOutput() {
+        StyledTextOutput output = Mock()
+        def event = new StyledTextOutputEvent(100, 'category', StyledTextOutput.Style.UserInput, 'message')
+
+        when:
+        event.render(output)
+
+        then:
+        1 * output.style(StyledTextOutput.Style.UserInput)
+        1 * output.text('message')
+        0 * output._
+    }
+    
+    def rendersMultipleSpansToTextOutput() {
+        StyledTextOutput output = Mock()
+        List spans = [new StyledTextOutputEvent.Span(StyledTextOutput.Style.UserInput, 'UserInput'),
+                new StyledTextOutputEvent.Span(StyledTextOutput.Style.Normal, 'Normal'),
+                new StyledTextOutputEvent.Span(StyledTextOutput.Style.Header, 'Header')
+        ]
+        def event = new StyledTextOutputEvent(100, 'category', spans)
+
+        when:
+        event.render(output)
+
+        then:
+        1 * output.style(StyledTextOutput.Style.UserInput)
+        1 * output.text('UserInput')
+        1 * output.style(StyledTextOutput.Style.Normal)
+        1 * output.text('Normal')
+        1 * output.style(StyledTextOutput.Style.Header)
+        1 * output.text('Header')
+        0 * output._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/TextStreamOutputEventListenerTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/TextStreamOutputEventListenerTest.groovy
new file mode 100644
index 0000000..5f7b312
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/logging/internal/TextStreamOutputEventListenerTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.logging.internal
+
+import spock.lang.Specification
+import org.gradle.api.logging.LogLevel
+
+class TextStreamOutputEventListenerTest extends Specification {
+    private final OutputEventListener target = Mock()
+    private final TextStreamOutputEventListener listener = new TextStreamOutputEventListener(target)
+
+    def attachesLogLevelToTextOutputEvent() {
+        StyledTextOutputEvent event = Mock()
+        StyledTextOutputEvent transformed = Mock()
+
+        when:
+        listener.onOutput(event)
+
+        then:
+        1 * event.withLogLevel(LogLevel.LIFECYCLE) >> transformed
+        1 * target.onOutput(transformed)
+        0 * target._
+    }
+
+    def doesNotChangeLogLevelWhenEventAlreadyHasALogLevel() {
+        StyledTextOutputEvent event = Mock()
+
+        when:
+        listener.onOutput(event)
+
+        then:
+        1 * event.logLevel >> LogLevel.INFO
+        1 * target.onOutput(event)
+        0 * target._
+    }
+
+    def doesNotForwardLogLevelChangeEvents() {
+        StyledTextOutputEvent event = Mock()
+        StyledTextOutputEvent transformed = Mock()
+
+        when:
+        listener.onOutput(new LogLevelChangeEvent(LogLevel.ERROR))
+        listener.onOutput(event)
+
+        then:
+        1 * event.withLogLevel(LogLevel.ERROR) >> transformed
+        1 * target.onOutput(transformed)
+        0 * target._
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy
new file mode 100644
index 0000000..529b5a5
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/MessageTest.groovy
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.messaging.remote.internal
+
+import spock.lang.Specification
+
+class MessageTest extends Specification {
+    private final GroovyClassLoader source = new GroovyClassLoader(getClass().getClassLoader());
+    private final GroovyClassLoader dest = new GroovyClassLoader(getClass().getClassLoader());
+
+    def replacesUnserializableExceptionWithPlaceholder() {
+        RuntimeException cause = new RuntimeException("nested");
+        UnserializableException original = new UnserializableException("message", cause);
+
+        when:
+        Object transported = transport(original);
+
+        then:
+        transported instanceof PlaceholderException
+        transported.message == UnserializableException.class.name + ": " + original.message
+        transported.stackTrace == original.stackTrace
+
+        transported.cause.class == RuntimeException.class
+        transported.cause.message == "nested"
+        transported.cause.stackTrace == cause.getStackTrace()
+    }
+
+    def replacesNestedUnserializableExceptionWithPlaceholder() {
+        Exception cause = new IOException("nested");
+        UnserializableException original = new UnserializableException("message", cause);
+        RuntimeException outer = new RuntimeException('message', original)
+
+        when:
+        Object transported = transport(outer);
+
+        then:
+        transported.class == RuntimeException.class
+        transported.message == 'message'
+        transported.stackTrace == outer.stackTrace
+
+        transported.cause instanceof PlaceholderException
+        transported.cause.message == UnserializableException.class.name + ": " + original.message
+        transported.cause.stackTrace == original.stackTrace
+
+        transported.cause.cause.class == IOException
+        transported.cause.cause.message == "nested"
+        transported.cause.cause.stackTrace == cause.stackTrace
+    }
+
+    def replacesUndeserializableExceptionWithPlaceholder() {
+        RuntimeException cause = new RuntimeException("nested");
+        UndeserializableException original = new UndeserializableException("message", cause);
+
+        when:
+        Object transported = transport(original);
+
+        then:
+        transported instanceof PlaceholderException
+        transported.message == UndeserializableException.class.name + ": " + original.message
+        transported.stackTrace == original.stackTrace
+
+        transported.cause.class == RuntimeException.class
+        transported.cause.message == "nested"
+        transported.cause.stackTrace == cause.stackTrace
+    }
+
+    def replacesNestedUndeserializableExceptionWithPlaceholder() {
+        RuntimeException cause = new RuntimeException("nested");
+        UndeserializableException original = new UndeserializableException("message", cause);
+        RuntimeException outer = new RuntimeException('message', original)
+
+        when:
+        Object transported = transport(outer);
+
+        then:
+        transported.class == RuntimeException
+        transported.message == 'message'
+        transported.stackTrace == outer.stackTrace
+
+        transported.cause instanceof PlaceholderException
+        transported.cause.message == UndeserializableException.class.name + ": " + original.message
+        transported.cause.stackTrace == original.stackTrace
+
+        transported.cause.cause.class == RuntimeException.class
+        transported.cause.cause.message == "nested"
+        transported.cause.cause.stackTrace == cause.stackTrace
+    }
+
+    def replacesUnserializableExceptionFieldWithPlaceholder() {
+        RuntimeException cause = new RuntimeException()
+        UndeserializableException original = new UndeserializableException("message", cause);
+        ExceptionWithExceptionField outer = new ExceptionWithExceptionField("nested", original)
+
+        when:
+        Object transported = transport(outer);
+
+        then:
+        transported instanceof ExceptionWithExceptionField
+
+        transported.throwable instanceof PlaceholderException
+        transported.throwable.message == UndeserializableException.class.name + ": " + original.message
+        transported.throwable.stackTrace == original.stackTrace
+
+        transported.throwable == transported.cause
+    }
+
+    def replacesIncompatibleExceptionWithLocalVersion() {
+        RuntimeException cause = new RuntimeException("nested");
+        Class<? extends RuntimeException> sourceExceptionType = source.parseClass(
+                "package org.gradle; public class TestException extends RuntimeException { public TestException(String msg, Throwable cause) { super(msg, cause); } }");
+        Class<? extends RuntimeException> destExceptionType = dest.parseClass(
+                "package org.gradle; public class TestException extends RuntimeException { private String someField; public TestException(String msg) { super(msg); } }");
+
+        RuntimeException original = sourceExceptionType.newInstance("message", cause);
+
+        when:
+        RuntimeException transported = transport(original);
+
+        then:
+        transported.class == destExceptionType
+        transported.message == original.message
+        transported.stackTrace == original.stackTrace
+
+        transported.cause.class == RuntimeException.class
+        transported.cause.message == "nested"
+        transported.cause.stackTrace == cause.stackTrace
+    }
+
+    def usesPlaceholderWhenLocalExceptionCannotBeConstructed() {
+        RuntimeException cause = new RuntimeException("nested");
+        Class<? extends RuntimeException> sourceExceptionType = source.parseClass(
+                "package org.gradle; public class TestException extends RuntimeException { public TestException(String msg, Throwable cause) { super(msg, cause); } }");
+        dest.parseClass("package org.gradle; public class TestException extends RuntimeException { private String someField; }");
+
+        RuntimeException original = sourceExceptionType.newInstance("message", cause);
+
+        when:
+        Object transported = transport(original);
+
+        then:
+        transported  instanceof PlaceholderException
+        transported.message == original.toString()
+        transported.stackTrace == original.stackTrace
+
+        transported.cause.class == RuntimeException.class
+        transported.cause.message == "nested"
+        transported.cause.stackTrace == cause.stackTrace
+    }
+
+    private Object transport(Object arg) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        new TestPayloadMessage(payload: arg).send(outputStream);
+
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+        def message = Message.receive(inputStream, dest);
+        return message.payload
+    }
+}
+
+private class TestPayloadMessage extends Message {
+    def payload
+}
+
+private class ExceptionWithExceptionField extends RuntimeException {
+    def Throwable throwable
+
+    def ExceptionWithExceptionField(String message, Throwable cause) {
+        super(message, cause)
+        throwable = cause
+    }
+}
+
+private class UnserializableException extends RuntimeException {
+    public UnserializableException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    private void writeObject(ObjectOutputStream outstr) throws IOException {
+        outstr.writeObject(new Object());
+    }
+}
+
+private class UndeserializableException extends RuntimeException {
+    public UndeserializableException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    private void readObject(ObjectInputStream outstr) throws ClassNotFoundException {
+        throw new ClassNotFoundException();
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/RemoteMethodInvocationTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/RemoteMethodInvocationTest.java
index 805efff..aaf3769 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/RemoteMethodInvocationTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/messaging/remote/internal/RemoteMethodInvocationTest.java
@@ -16,19 +16,14 @@
 
 package org.gradle.messaging.remote.internal;
 
-import groovy.lang.GroovyClassLoader;
 import org.junit.Test;
 
-import java.io.*;
-
-import static org.gradle.util.Matchers.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.gradle.util.Matchers.strictlyEqual;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
 
 public class RemoteMethodInvocationTest {
-    private final GroovyClassLoader source = new GroovyClassLoader(getClass().getClassLoader());
-    private final GroovyClassLoader dest = new GroovyClassLoader(getClass().getClassLoader());
-
     @Test
     public void equalsAndHashCode() throws Exception {
         RemoteMethodInvocation invocation = new RemoteMethodInvocation(1, new Object[]{"param"});
@@ -39,140 +34,4 @@ public class RemoteMethodInvocationTest {
         assertThat(invocation, not(equalTo(differentMethod)));
         assertThat(invocation, not(equalTo(differentArgs)));
     }
-
-    @Test
-    public void replacesUnserializableExceptionWithPlaceholder() throws Exception {
-        RuntimeException cause = new RuntimeException("nested");
-        UnserializableException original = new UnserializableException("message", cause);
-        Object transported = transport(original);
-
-        assertThat(transported, instanceOf(PlaceholderException.class));
-        PlaceholderException e = (PlaceholderException) transported;
-        assertThat(e.getMessage(), equalTo(UnserializableException.class.getName() + ": " + original.getMessage()));
-        assertThat(e.getStackTrace(), equalTo(original.getStackTrace()));
-
-        assertThat(e.getCause().getClass(), equalTo((Object) RuntimeException.class));
-        assertThat(e.getCause().getMessage(), equalTo("nested"));
-        assertThat(e.getCause().getStackTrace(), equalTo(cause.getStackTrace()));
-    }
-
-    @Test
-    public void replacesNestedUnserializableExceptionWithPlaceholder() throws Exception {
-        Exception cause = new IOException("nested");
-        UnserializableException original = new UnserializableException("message", cause);
-        Object transported = transport(new RuntimeException(original));
-
-        assertThat(transported, instanceOf(RuntimeException.class));
-        PlaceholderException e = (PlaceholderException) ((RuntimeException) transported).getCause();
-        assertThat(e.getMessage(), equalTo(UnserializableException.class.getName() + ": " + original.getMessage()));
-        assertThat(e.getStackTrace(), equalTo(original.getStackTrace()));
-
-        assertThat(e.getCause().getClass(), equalTo((Object) IOException.class));
-        assertThat(e.getCause().getMessage(), equalTo("nested"));
-        assertThat(e.getCause().getStackTrace(), equalTo(cause.getStackTrace()));
-    }
-
-    @Test
-    public void replacesUndeserializableExceptionWithPlaceholder() throws Exception {
-        RuntimeException cause = new RuntimeException("nested");
-        UndeserializableException original = new UndeserializableException("message", cause);
-        Object transported = transport(original);
-
-        assertThat(transported, instanceOf(RuntimeException.class));
-        PlaceholderException e = (PlaceholderException) transported;
-        assertThat(e.getMessage(), equalTo(UndeserializableException.class.getName() + ": " + original.getMessage()));
-        assertThat(e.getStackTrace(), equalTo(original.getStackTrace()));
-
-        assertThat(e.getCause().getClass(), equalTo((Object) RuntimeException.class));
-        assertThat(e.getCause().getMessage(), equalTo("nested"));
-        assertThat(e.getCause().getStackTrace(), equalTo(cause.getStackTrace()));
-    }
-
-    @Test
-    public void replacesNestedUndeserializableExceptionWithPlaceholder() throws Exception {
-        RuntimeException cause = new RuntimeException("nested");
-        UndeserializableException original = new UndeserializableException("message", cause);
-        Object transported = transport(new RuntimeException(original));
-
-        assertThat(transported, instanceOf(RuntimeException.class));
-        PlaceholderException e = (PlaceholderException) ((RuntimeException) transported).getCause();
-        assertThat(e.getMessage(), equalTo(UndeserializableException.class.getName() + ": " + original.getMessage()));
-        assertThat(e.getStackTrace(), equalTo(original.getStackTrace()));
-
-        assertThat(e.getCause().getClass(), equalTo((Object) RuntimeException.class));
-        assertThat(e.getCause().getMessage(), equalTo("nested"));
-        assertThat(e.getCause().getStackTrace(), equalTo(cause.getStackTrace()));
-    }
-
-    @Test
-    public void replacesIncompatibleExceptionWithLocalVersion() throws Exception {
-        RuntimeException cause = new RuntimeException("nested");
-        Class<? extends RuntimeException> sourceExceptionType = source.parseClass(
-                "package org.gradle; public class TestException extends RuntimeException { public TestException(String msg, Throwable cause) { super(msg, cause); } }");
-        Class<? extends RuntimeException> destExceptionType = dest.parseClass(
-                "package org.gradle; public class TestException extends RuntimeException { private String someField; public TestException(String msg) { super(msg); } }");
-
-        RuntimeException original = sourceExceptionType.getConstructor(String.class, Throwable.class).newInstance("message", cause);
-        Object transported = transport(original);
-
-        assertThat(transported, instanceOf(destExceptionType));
-        RuntimeException e = (RuntimeException) transported;
-        assertThat(e.getMessage(), equalTo(original.getMessage()));
-        assertThat(e.getStackTrace(), equalTo(original.getStackTrace()));
-
-        assertThat(e.getCause().getClass(), equalTo((Object) RuntimeException.class));
-        assertThat(e.getCause().getMessage(), equalTo("nested"));
-        assertThat(e.getCause().getStackTrace(), equalTo(cause.getStackTrace()));
-    }
-
-    @Test
-    public void usesPlaceholderWhenLocalExceptionCannotBeConstructed() throws Exception {
-        RuntimeException cause = new RuntimeException("nested");
-        Class<? extends RuntimeException> sourceExceptionType = source.parseClass(
-                "package org.gradle; public class TestException extends RuntimeException { public TestException(String msg, Throwable cause) { super(msg, cause); } }");
-        Class<? extends RuntimeException> destExceptionType = dest.parseClass(
-                "package org.gradle; public class TestException extends RuntimeException { private String someField; }");
-
-        RuntimeException original = sourceExceptionType.getConstructor(String.class, Throwable.class).newInstance("message", cause);
-        Object transported = transport(original);
-
-        assertThat(transported, instanceOf(PlaceholderException.class));
-        RuntimeException e = (RuntimeException) transported;
-        assertThat(e.getMessage(), equalTo(original.toString()));
-        assertThat(e.getStackTrace(), equalTo(original.getStackTrace()));
-
-        assertThat(e.getCause().getClass(), equalTo((Object) RuntimeException.class));
-        assertThat(e.getCause().getMessage(), equalTo("nested"));
-        assertThat(e.getCause().getStackTrace(), equalTo(cause.getStackTrace()));
-    }
-
-    private Object transport(Object arg) throws Exception {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        new RemoteMethodInvocation(1, new Object[]{arg}).send(outputStream);
-
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
-        RemoteMethodInvocation invocation = (RemoteMethodInvocation) Message.receive(inputStream, dest);
-        return invocation.getArguments()[0];
-    }
-
-    private static class UnserializableException extends RuntimeException {
-        public UnserializableException(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        private void writeObject(ObjectOutputStream outstr) throws IOException {
-            outstr.writeObject(new Object());
-        }
-    }
-
-    private static class UndeserializableException extends RuntimeException {
-        public UndeserializableException(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        private void readObject(ObjectInputStream outstr) throws ClassNotFoundException {
-            throw new ClassNotFoundException();
-        }
-    }
-
 }
\ No newline at end of file
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java b/subprojects/gradle-core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java
index dca5319..e36da5f 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/process/internal/DefaultWorkerProcessFactoryTest.java
@@ -61,7 +61,7 @@ public class DefaultWorkerProcessFactoryTest {
             ignoring(fileResolver);
         }});
 
-        WorkerProcessBuilder builder = factory.newProcess();
+        WorkerProcessBuilder builder = factory.create();
 
         assertThat(builder.getJavaCommand().getMain(), equalTo(GradleWorkerMain.class.getName()));
         assertThat(builder.getLogLevel(), equalTo(LogLevel.LIFECYCLE));
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/DisconnectableInputStreamTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/util/DisconnectableInputStreamTest.groovy
new file mode 100644
index 0000000..e875dfe
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/DisconnectableInputStreamTest.groovy
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.util
+
+import java.util.concurrent.CopyOnWriteArrayList
+import org.jmock.integration.junit4.JMock
+import org.junit.Test
+import org.junit.runner.RunWith
+import static org.hamcrest.Matchers.equalTo
+import static org.hamcrest.Matchers.greaterThan
+import static org.junit.Assert.assertThat
+
+ at RunWith(JMock)
+class DisconnectableInputStreamTest extends MultithreadedTestCase {
+    final JUnit4GroovyMockery context = new JUnit4GroovyMockery()
+
+    @Test
+    public void inputStreamReadsFromSourceInputStream() {
+        def instr = new DisconnectableInputStream(stream("some text"), executorFactory)
+
+        assertReads(instr, "some text")
+
+        def nread = instr.read(new byte[20])
+        assertThat(nread, equalTo(-1))
+
+        instr.close()
+    }
+
+    @Test
+    public void buffersDataReadFromSourceInputStream() {
+        def instr = new DisconnectableInputStream(stream("test1test2end"), executorFactory)
+
+        assertReads(instr, "test1")
+        assertReads(instr, "test2")
+        assertReads(instr, "end")
+
+        def nread = instr.read(new byte[20])
+        assertThat(nread, equalTo(-1))
+
+        instr.close()
+    }
+
+    @Test
+    public void canReadSingleChars() {
+        def instr = new DisconnectableInputStream(stream("abc"), executorFactory)
+
+        assertThat((char)instr.read(), equalTo('a'.charAt(0)))
+        assertThat((char)instr.read(), equalTo('b'.charAt(0)))
+        assertThat((char)instr.read(), equalTo('c'.charAt(0)))
+        assertThat(instr.read(), equalTo(-1))
+
+        instr.close()
+    }
+
+    @Test
+    public void canReadUsingZeroLengthBuffer() {
+        def instr = new DisconnectableInputStream(stream("abc"), executorFactory)
+
+        assertThat(instr.read(new byte[0], 0, 0), equalTo(0))
+        assertReads(instr, "abc")
+        assertThat(instr.read(new byte[0], 0, 0), equalTo(-1))
+
+        instr.close()
+    }
+
+    @Test
+    public void canFillAndEmptyBufferInChunks() {
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            System.arraycopy('aaaaaa'.bytes, 0, buffer, pos, 6)
+            return 6
+        }
+        source.onRead { buffer, pos, count ->
+            syncAt(1)
+            syncAt(2)
+            System.arraycopy('aaaa'.bytes, 0, buffer, pos, 4)
+            return 4
+        }
+        source.onRead { buffer, pos, count ->
+            syncAt(3)
+            System.arraycopy('aa'.bytes, 0, buffer, pos, 2)
+            syncAt(4)
+            return 2
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory, 10)
+
+        run {
+            syncAt(1)
+            assertReads(instr, "aaaa")
+            syncAt(2)
+            assertReads(instr, "aaaaaa")
+            syncAt(3)
+            syncAt(4)
+            assertReads(instr, "aa")
+        }
+
+        instr.close()
+    }
+
+    @Test
+    public void readBlocksUntilDataIsAvailable() {
+
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            byte[] expected = "some text".bytes
+            System.arraycopy(expected, 0, buffer, pos, expected.length)
+            syncAt(1)
+            return expected.length
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory)
+        run {
+            expectBlocksUntil(1) {
+                assertReads(instr, "some text")
+            }
+        }
+
+        instr.close()
+    }
+
+    @Test
+    public void readBlocksUntilInputStreamIsClosed() {
+        clockTick(1).hasParticipants(3)
+        clockTick(2).hasParticipants(3)
+
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            syncAt(1)
+            syncAt(2)
+            return count
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory)
+
+        start {
+            expectBlocksUntil(1) {
+                def nread = instr.read(new byte[20])
+                assertThat(nread, equalTo(-1))
+            }
+            syncAt(2)
+        }
+
+        run {
+            syncAt(1)
+            instr.close()
+            syncAt(2)
+        }
+    }
+
+    @Test
+    public void readBlocksUntilEndOfInputReached() {
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            syncAt(1)
+            return -1
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory)
+
+        run {
+            expectBlocksUntil(1) {
+                def nread = instr.read(new byte[20])
+                assertThat(nread, equalTo(-1))
+            }
+        }
+
+        instr.close()
+    }
+
+    @Test
+    public void readerBlocksUntilReaderReceivesReadException() {
+        IOException failure = new IOException("failed")
+
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            throw failure
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory)
+
+        run {
+            def nread = instr.read(new byte[20])
+            assertThat(nread, equalTo(-1))
+        }
+    }
+
+    @Test
+    public void readerThreadBlocksWhenBufferFull() {
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            System.arraycopy('abcdefghij'.bytes, 0, buffer, pos, 10)
+            expectLater(1)
+            return 10
+        }
+        source.onRead { buffer, pos, count ->
+            shouldBeAt(1)
+            return -1
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory, 10)
+
+        run {
+            syncAt(1)
+            assertReads(instr, 'abc')
+            assertReads(instr, 'defghi')
+            assertReads(instr, 'j')
+        }
+
+        instr.close()
+    }
+    
+    @Test
+    public void readerThreadStopsReadingAfterClose() {
+        def source = stream()
+        source.onRead { buffer, pos, count ->
+            return count
+        }
+
+        def instr = new DisconnectableInputStream(source, executorFactory)
+        instr.read()
+        instr.close()
+
+        waitForAll()
+    }
+
+    @Test
+    public void cannotReadFromInputStreamAfterItIsClosed() {
+        def instr = new DisconnectableInputStream(stream("some text"), executorFactory)
+        instr.close()
+
+        assertThat(instr.read(), equalTo(-1))
+        assertThat(instr.read(new byte[10]), equalTo(-1))
+        assertThat(instr.read(new byte[10], 2, 5), equalTo(-1))
+    }
+
+    def assertReads(InputStream instr, String expected) {
+        def expectedBytes = expected.bytes
+        def buffer = new byte[expectedBytes.length]
+        def remaining = expectedBytes.length
+        while (remaining > 0) {
+            def nread = instr.read(buffer, expectedBytes.length - remaining, remaining)
+            assertThat(nread, greaterThan(0))
+            remaining -= nread
+        }
+        assertThat(new String(buffer), equalTo(expected))
+    }
+
+    def stream(String text) {
+        return new ByteArrayInputStream(text.bytes)
+    }
+
+    def stream() {
+        return new ActionInputStream()
+    }
+}
+
+class ActionInputStream extends InputStream {
+    List<Closure> actions = new CopyOnWriteArrayList<Closure>()
+
+    def onRead(Closure cl) {
+        actions << cl
+    }
+
+    @Override
+    int read(byte[] bytes, int pos, int count) {
+        if (actions.isEmpty()) {
+            return -1
+        }
+        Closure action = actions.remove(0)
+        return action.call([bytes, pos, count])
+    }
+
+    @Override
+    int read() {
+        throw new UnsupportedOperationException()
+    }
+}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
index be9ef82..0d691c7 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/GradleVersionTest.groovy
@@ -39,14 +39,12 @@ class GradleVersionTest {
 Gradle $version.version
 ------------------------------------------------------------
 
-Gradle buildtime: $version.buildTime
+Gradle build time: $version.buildTime
 Groovy: $InvokerHelper.version
 Ant: $Main.antVersion
 Ivy: ${Ivy.ivyVersion}
-Java: ${System.getProperty("java.version")}
-JVM: ${System.getProperty("java.vm.version")}
-JVM Vendor: ${System.getProperty("java.vm.vendor")}
-OS Name: ${System.getProperty("os.name")}
+JVM: ${Jvm.current()}
+OS: ${OperatingSystem.current()}
 """
         assertEquals(expectedText, version.prettyPrint())
     }
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/HelperUtil.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/util/HelperUtil.groovy
index 8ab6c8c..a72201a 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/util/HelperUtil.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/HelperUtil.groovy
@@ -86,7 +86,7 @@ class HelperUtil {
                 projectDir ?: new File(parentProject.getProjectDir(), name),
                 new StringScriptSource("test build file", null),
                 parentProject.gradle,
-                parentProject.gradle.serviceRegistryFactory
+                parentProject.gradle.services
         )
         parentProject.addChildProject project
         parentProject.projectRegistry.addProject project
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/JUnit4GroovyMockery.java b/subprojects/gradle-core/src/test/groovy/org/gradle/util/JUnit4GroovyMockery.java
index 5527805..71da9a0 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/util/JUnit4GroovyMockery.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/JUnit4GroovyMockery.java
@@ -30,6 +30,7 @@ import org.jmock.api.Invocation;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.jmock.lib.legacy.ClassImposteriser;
 
+import java.lang.reflect.Field;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -52,11 +53,26 @@ public class JUnit4GroovyMockery extends JUnit4Mockery {
     public <T> T mock(Class<T> typeToMock, String name) {
         names.putIfAbsent(name, new AtomicInteger());
         int count = names.get(name).getAndIncrement();
+        T mock;
         if (count == 0) {
-            return super.mock(typeToMock, name);
+            mock = super.mock(typeToMock, name);
         } else {
-            return super.mock(typeToMock, name + count);
+            mock = super.mock(typeToMock, name + count);
         }
+        if (mock instanceof ClassLoader) {
+            for (Field field : ClassLoader.class.getDeclaredFields()) {
+                if (field.getName().equals("initialized")) {
+                    field.setAccessible(true);
+                    try {
+                        field.set(mock, true);
+                    } catch (IllegalAccessException e) {
+                        throw UncheckedException.asUncheckedException(e);
+                    }
+                    break;
+                }
+            }
+        }
+        return mock;
     }
 
     class ClosureExpectations extends Expectations {
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.groovy
index 5dce686..aec53b3 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.groovy
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultiParentClassLoaderTest.groovy
@@ -16,12 +16,11 @@
 package org.gradle.util
 
 import org.jmock.integration.junit4.JMock
-import static org.hamcrest.Matchers.*
-import static org.junit.Assert.*
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.jmock.lib.legacy.ClassImposteriser
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
 
 @RunWith(JMock.class)
 class MultiParentClassLoaderTest {
@@ -32,9 +31,8 @@ class MultiParentClassLoaderTest {
 
     @Before
     public void setup() {
-        context.imposteriser = ClassImposteriser.INSTANCE
-        parent1 = context.mock(ClassLoader, 'parent1')
-        parent2 = context.mock(ClassLoader, 'parent2')
+        parent1 = context.mock(ClassLoader)
+        parent2 = context.mock(ClassLoader)
         loader = new MultiParentClassLoader(parent1, parent2)
     }
 
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultithreadedTestCase.java b/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultithreadedTestCase.java
index 49f2a2f..ac9483d 100644
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultithreadedTestCase.java
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/MultithreadedTestCase.java
@@ -335,7 +335,7 @@ public class MultithreadedTestCase {
      *
      * @param tick The expected clock tick.
      */
-    private void expectLater(final int tick) {
+    public void expectLater(final int tick) {
         final Thread targetThread = Thread.currentThread();
         LOGGER.debug("Thread {} expecting tick {}", targetThread, tick);
         start(new Runnable() {
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/PathHelperTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/util/PathHelperTest.groovy
deleted file mode 100644
index bda0d0e..0000000
--- a/subprojects/gradle-core/src/test/groovy/org/gradle/util/PathHelperTest.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-package org.gradle.util
-
-import static org.junit.Assert.assertFalse
-import static org.junit.Assert.assertTrue
-import org.junit.Test
-
-/**
- * @author Hans Dockter
- */
-class PathHelperTest {
-    @Test public void absolutePath() {
-        assertTrue(PathHelper.isAbsolutePath(":path"))
-        assertFalse(PathHelper.isAbsolutePath("path"))
-    }
-}
diff --git a/subprojects/gradle-core/src/test/groovy/org/gradle/util/PathTest.groovy b/subprojects/gradle-core/src/test/groovy/org/gradle/util/PathTest.groovy
new file mode 100644
index 0000000..14f71e3
--- /dev/null
+++ b/subprojects/gradle-core/src/test/groovy/org/gradle/util/PathTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package org.gradle.util
+
+import spock.lang.Specification
+import static org.gradle.util.Matchers.*
+
+/**
+ * @author Hans Dockter
+ */
+class PathTest extends Specification {
+    def construction() {
+        expect:
+        Path.path(':').is(Path.ROOT)
+        Path.ROOT.getPath() == ':'
+        Path.path('a').getPath() == 'a'
+        Path.path('a:b:c').getPath() == 'a:b:c'
+        Path.path(':a').getPath() == ':a'
+        Path.path(':a:b').getPath() == ':a:b'
+    }
+    
+    def equalsAndHashCode() {
+        expect:
+        strictlyEquals(Path.ROOT, Path.ROOT)
+        strictlyEquals(Path.path('path'), Path.path('path'))
+        Path.path(':a') != Path.path(':b')
+    }
+
+    def canGetParent() {
+        expect:
+        Path.path(':a:b').parent == Path.path(':a')
+        Path.path(':a').parent == Path.path(':')
+        Path.path(':').parent == null
+        Path.path('a:b').parent == Path.path('a')
+        Path.path('a').parent == null
+    }
+
+    def canGetName() {
+        expect:
+        Path.path(':a:b').name == 'b'
+        Path.path(':a').name == 'a'
+        Path.path(':').name == null
+        Path.path('a:b').name == 'b'
+        Path.path('a').name == 'a'
+    }
+
+    def convertsRelativePathToAbsolutePath() {
+        expect:
+        def Path path = Path.path(':')
+        path.absolutePath('path') == ':path'
+        path.resolve('path') == Path.path(':path')
+
+        path = Path.path(':sub')
+        path.absolutePath('path') == ':sub:path'
+        path.resolve('path') == Path.path(':sub:path')
+    }
+
+    def convertsAbsolutePathToAbsolutePath() {
+        def Path path = Path.path(':')
+
+        expect:
+        path.absolutePath(':') == ':'
+        path.absolutePath(':path') == ':path'
+        path.resolve(':') == Path.path(':')
+        path.resolve(':path') == Path.path(':path')
+    }
+
+    def convertsAbsolutePathToRelativePath() {
+        expect:
+        def path = Path.path(':')
+        path.relativePath(':') == ':'
+        path.relativePath(':path') == 'path'
+
+        path = Path.path(':sub')
+        path.relativePath(':') == ':'
+        path.relativePath(':sub') == ':sub'
+        path.relativePath(':sub:path') == 'path'
+        path.relativePath(':sub2:path') == ':sub2:path'
+        path.relativePath(':other:path') == ':other:path'
+    }
+
+    def convertsRelativePathToRelativePath() {
+        def Path path = Path.path(':')
+
+        expect:
+        path.relativePath('path') == 'path'
+    }
+
+    def sortsPathsDepthFirstCaseInsensitive() {
+        expect:
+        paths(['a', 'b', 'A', 'abc']).sort() == paths(['A', 'a', 'abc', 'b'])
+        paths([':a', ':b', ':b:a', ':B:a', ':', ':B', ':a:a']).sort() == paths([':', ':a', ':a:a', ':B', ':B:a', ':b', ':b:a'])
+        paths(['b', 'b:a', 'a', 'a:a']).sort() == paths(['a', 'a:a', 'b', 'b:a'])
+        paths([':', ':a', 'a']).sort() == paths(['a', ':', ':a'])
+    }
+
+    def paths(List<String> paths) {
+        return paths.collect { Path.path(it) }
+    }
+}
diff --git a/subprojects/gradle-docs/docs.gradle b/subprojects/gradle-docs/docs.gradle
index 78c73cb..d8a182a 100644
--- a/subprojects/gradle-docs/docs.gradle
+++ b/subprojects/gradle-docs/docs.gradle
@@ -17,6 +17,8 @@
 import org.gradle.build.docs.UserGuideTransformTask
 import org.gradle.build.docs.ExtractSnippetsTask
 import org.gradle.build.docs.AssembleSamplesDocTask
+import org.gradle.build.docs.dsl.AssembleDslDocTask
+import org.gradle.build.docs.dsl.ExtractDslMetaDataTask
 
 apply plugin: 'base'
 
@@ -54,6 +56,7 @@ distDocsDir = new File(buildDir, 'distDocs')
 samplesDir = file("$buildDir/samples")
 docbookSrc = new File(project.buildDir, 'src/docbook')
 samplesSrcDir = file('src/samples')
+websiteDocs = new File(buildDir, 'websiteDocs')
 
 tasks.withType(Docbook2Xhtml).allObjects { task->
     task.dependsOn userguideStyleSheets
@@ -65,6 +68,10 @@ tasks.withType(UserGuideTransformTask).allObjects { task->
     task.dependsOn samples
     task.snippetsDir = samples.snippetsDir
 }
+tasks.withType(AssembleDslDocTask).allObjects { task ->
+    task.classpath = configurations.userGuideTask
+    task.classDocbookDir = new File(userguideSrcDir, 'dsl')
+}
 
 task samples(type: ExtractSnippetsTask) {
     source samplesSrcDir
@@ -109,8 +116,46 @@ task samplesDocs(type: Docbook2Xhtml, dependsOn: samplesDocbook) {
     stylesheetName = 'standaloneHtml.xsl'
 }
 
-task userguideDocbook(type: UserGuideTransformTask, dependsOn: [samples, samplesDocbook]) {
+task dslMetaData(type: ExtractDslMetaDataTask) {
+    source { groovydoc.source }
+    destFile = new File(docbookSrc, 'dsl-meta-data.bin')
+}
+
+task dslDocbook(type: AssembleDslDocTask, dependsOn: [dslMetaData]) {
+    inputs.files fileTree(dir: userguideSrcDir, includes: ['dsl/*.xml'])
+    sourceFile = new File(userguideSrcDir, 'dsl/dsl.xml')
+    classMetaDataFile = dslMetaData.destFile
+    pluginsMetaDataFile = new File(userguideSrcDir, 'dsl/plugins.xml')
+    destFile = new File(docbookSrc, 'dsl.xml')
+}
+
+task dslStandaloneDocbook(type: UserGuideTransformTask, dependsOn: [dslDocbook]) {
+    inputs.files fileTree(dir: userguideSrcDir, includes: ['*.xml'])
+    inputs.files fileTree(dir: docbookSrc, includes: ['*.xml'])
+    sourceFile = dslDocbook.destFile
+    destFile = new File(docbookSrc, 'dsl-standalone.xml')
+    javadocUrl = '../javadoc'
+    groovydocUrl = '../groovydoc'
+    websiteUrl = 'http://www.gradle.org'
+}
+
+task dslHtml(type: Docbook2Xhtml, dependsOn: dslStandaloneDocbook) {
+    source dslStandaloneDocbook.destFile
+    destDir = new File(docsDir, 'dsl')
+    stylesheetName = 'userGuideHtml.xsl'
+    resources = fileTree {
+        from userguideSrcDir
+        include 'img/*.png'
+    }
+    resources += fileTree {
+        from cssSrcDir
+        include '*.css'
+    }
+}
+
+task userguideDocbook(type: UserGuideTransformTask, dependsOn: [samples, samplesDocbook, dslDocbook]) {
     inputs.files fileTree(dir: userguideSrcDir, includes: ['*.xml'])
+    inputs.files fileTree(dir: docbookSrc, includes: ['*.xml'])
     sourceFile = new File(userguideSrcDir, 'userguide.xml')
     destFile = new File(docbookSrc, 'userguide.xml')
     javadocUrl = '../javadoc'
@@ -119,6 +164,8 @@ task userguideDocbook(type: UserGuideTransformTask, dependsOn: [samples, samples
 }
 
 task remoteUserguideDocbook(type: UserGuideTransformTask, dependsOn: samples) {
+    inputs.files fileTree(dir: userguideSrcDir, includes: ['*.xml'])
+    inputs.files fileTree(dir: docbookSrc, includes: ['*.xml'])
     sourceFile = new File(userguideSrcDir, 'userguide.xml')
     destFile = new File(docbookSrc, 'remoteUserguide.xml')
     doFirst {
@@ -171,12 +218,14 @@ task userguideXhtml(type: Docbook2Xhtml, dependsOn: remoteUserguideDocbook) {
 }
 
 task userguidePdf(type: Xhtml2Pdf, dependsOn: userguideXhtml) {
+    inputs.dir cssSrcDir
     sourceFile = userguideXhtml.destFile
     destFile = new File(userguideDir, 'userguide.pdf')
     classpath = configurations.userGuideTask
 }
 
 task javadoc(type: Javadoc) {
+    group = 'documentation'
     source groovyProjects().collect {project -> project.sourceSets.main.allJava }
     destinationDir = new File(docsDir, 'javadoc')
     classpath = files(groovyProjects().collect {project -> [project.sourceSets.main.compileClasspath, project.sourceSets.main.classes] })
@@ -194,7 +243,15 @@ task javadoc(type: Javadoc) {
     }
 }
 
+task checkstyleApi(type: Checkstyle) {
+    source javadoc.source
+    configFile = file("$checkstyleConfigDir/checkstyle-api.xml")
+    classpath = files()
+    resultFile = file("$checkstyleResultsDir/checkstyle-api.xml")
+}
+
 task groovydoc(type: Groovydoc) {
+    group = 'documentation'
     source groovyProjects().collect {project -> project.sourceSets.main.groovy }
     destinationDir = new File(docsDir, 'groovydoc')
     includes = javadoc.includes
@@ -222,8 +279,9 @@ task distDocs(type: Docbook2Xhtml, dependsOn: userguideFragmentSrc) {
     stylesheetName = 'standaloneHtml.xsl'
 }
 
-task websiteDocsSrc(type: UserGuideTransformTask, dependsOn: [userguideStyleSheets, samples, samplesDocbook]) {
+task websiteUserguideSrc(type: UserGuideTransformTask, dependsOn: [userguideStyleSheets, samples, samplesDocbook, dslDocbook]) {
     inputs.files fileTree(dir: userguideSrcDir, includes: ['*.xml'])
+    inputs.files fileTree(dir: docbookSrc, includes: ['*.xml'])
     sourceFile = new File(userguideSrcDir, 'userguide.xml')
     destFile = new File(docbookSrc, 'website.xml')
     tags << 'website'
@@ -234,9 +292,8 @@ task websiteDocsSrc(type: UserGuideTransformTask, dependsOn: [userguideStyleShee
     }
 }
 
-task websiteDocs(type: Docbook2Xhtml, dependsOn: websiteDocsSrc) {
-    File websiteDocs = new File(buildDir, 'websiteDocs')
-    source websiteDocsSrc.destFile
+task websiteUserguide(type: Docbook2Xhtml, dependsOn: websiteUserguideSrc) {
+    source websiteUserguideSrc.destFile
     destFile = new File(websiteDocs, 'website.html')
     stylesheetName = 'websiteHtml.xsl'
     resources = fileTree {
@@ -249,9 +306,33 @@ task websiteDocs(type: Docbook2Xhtml, dependsOn: websiteDocsSrc) {
     }
 }
 
-task userguide(dependsOn: [userguideHtml, userguideSingleHtml, userguidePdf], description: 'Generates the userguide')
+task websiteProperties {
+    def propertiesFile = new File(websiteDocs, 'version.properties')
+    inputs.property 'version', { version.toString() }
+    outputs.files propertiesFile
+    doLast {
+        def properties = new Properties()
+        properties.version = version.toString()
+        propertiesFile.parentFile.mkdirs()
+        propertiesFile.withWriter { writer -> properties.store(writer, 'documentation version properties') }
+    }
+}
+
+task websiteDocs {
+    dependsOn websiteUserguide, websiteProperties
+}
+
+task userguide {
+    dependsOn userguideHtml, userguideSingleHtml, userguidePdf
+    description = 'Generates the userguide'
+    group = 'documentation'
+}
 
-task docs(dependsOn: [javadoc, groovydoc, userguide, distDocs, samplesDocs])
+task docs {
+    dependsOn javadoc, groovydoc, userguide, distDocs, samplesDocs
+    description = 'Generates all documentation'
+    group = 'documentation'
+}
 
 task uploadDocs(dependsOn: docs) << {
     ftp(action: 'mkdir', remotedir: remoteLocations.docsRemoteDir)
@@ -380,7 +461,7 @@ class Xhtml2Pdf extends DefaultTask {
     FileCollection classpath
 
     def Xhtml2Pdf() {
-        onlyIf { !System.getProperty("os.name").toLowerCase().contains('windows') }
+        onlyIf { !OperatingSystem.current().isWindows() }
     }
 
     @TaskAction
diff --git a/subprojects/gradle-docs/src/docs/css/print.css b/subprojects/gradle-docs/src/docs/css/print.css
index 0bb46dd..1854108 100644
--- a/subprojects/gradle-docs/src/docs/css/print.css
+++ b/subprojects/gradle-docs/src/docs/css/print.css
@@ -76,7 +76,7 @@ a {
     color: black;
 }
 
-.book > .titlepage > .title {
+.book .titlepage div.title {
     text-align: right;
     page-break-after: always;
     border: none;
@@ -84,7 +84,7 @@ a {
     margin: 0;
     padding-top: 24%;
     padding-left: 0;
-    padding-right: 0;
+    padding-right: 5%;
     padding-bottom: 0;
 }
 
diff --git a/subprojects/gradle-docs/src/docs/stylesheets/userGuideHtmlCommon.xsl b/subprojects/gradle-docs/src/docs/stylesheets/userGuideHtmlCommon.xsl
index c725057..d341900 100644
--- a/subprojects/gradle-docs/src/docs/stylesheets/userGuideHtmlCommon.xsl
+++ b/subprojects/gradle-docs/src/docs/stylesheets/userGuideHtmlCommon.xsl
@@ -122,7 +122,9 @@
             <xsl:apply-templates select="." mode="class.value"/>
         </xsl:param>
         <div class="{$class}">
-            <xsl:call-template name="formal.object.heading"/>
+            <xsl:if test="title">
+                <xsl:call-template name="formal.object.heading"/>
+            </xsl:if>
             <div class="{$class}-contents">
                 <table>
                     <xsl:copy-of select="@*[not(local-name()='id')]"/>
diff --git a/subprojects/gradle-docs/src/docs/userguide/antlrPlugin.xml b/subprojects/gradle-docs/src/docs/userguide/antlrPlugin.xml
index b57bf08..7677233 100644
--- a/subprojects/gradle-docs/src/docs/userguide/antlrPlugin.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/antlrPlugin.xml
@@ -130,7 +130,52 @@
 
     <section>
         <title>Convention properties</title>
-        <para>TBD</para>
+        <para>The Antlr plugin does not add any convention properties.</para>
     </section>
-    
+
+    <section>
+        <title>Source set properties</title>
+        <para>The Antlr plugin adds the following properties to each source set in the project.</para>
+        <table>
+            <title>Antlr plugin - source set properties</title>
+            <thead>
+                <tr>
+                    <td>Property name</td>
+                    <td>Type</td>
+                    <td>Default value</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>
+                    <literal>antlr</literal>
+                </td>
+                <td>
+                    <apilink class="org.gradle.api.file.SourceDirectorySet"/> (read-only)
+                </td>
+                <td>
+                    Not null
+                </td>
+                <td>
+                    The Antlr grammar files of this source set. Contains all <filename>.g</filename> found in the Antlr
+                    source directories, and excludes all other types of files.
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <literal>antlr.srcDirs</literal>
+                </td>
+                <td>
+                    <classname>Set<File></classname>. Can set using anything described in <xref linkend="sec:specifying_multiple_files"/>.
+                </td>
+                <td>
+                    <literal>[<replaceable>projectDir</replaceable>/src/<replaceable>name</replaceable>/antlr]</literal>
+                </td>
+                <td>
+                    The source directories containing the Antlr grammar files of this source set.
+                </td>
+            </tr>
+        </table>
+    </section>
+
 </chapter>
diff --git a/subprojects/gradle-docs/src/docs/userguide/commandLine.xml b/subprojects/gradle-docs/src/docs/userguide/commandLine.xml
index 3d932d9..0491c7e 100644
--- a/subprojects/gradle-docs/src/docs/userguide/commandLine.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/commandLine.xml
@@ -19,7 +19,7 @@
         <cmdsynopsis>
             <command>gradle</command>
             <arg choice="opt" rep="repeat">option</arg>
-            <arg choice="opt" rep="repeat">task-name</arg>
+            <arg choice="opt" rep="repeat">task</arg>
         </cmdsynopsis>
         The command-line options available for the <command>gradle</command> command are listed below:
     </para>
@@ -104,9 +104,8 @@
             <listitem><para>Runs the build with all task actions disabled.</para> </listitem>
         </varlistentry>
         <varlistentry>
-            <term><option>-n</option>, <option>--dependencies</option></term>
-            <listitem><para>Show list of all project dependencies. See <xref linkend="sec:listing_dependencies"/>.
-            </para></listitem>
+            <term><option>--no-color</option></term>
+            <listitem><para>Do not use color in the console output.</para></listitem>
         </varlistentry>
         <varlistentry>
             <term><option>-p</option>, <option>--project-dir</option></term>
@@ -115,12 +114,14 @@
             </para></listitem>
         </varlistentry>
         <varlistentry>
-            <term><option>-q</option>, <option>--quiet</option></term>
-            <listitem><para>Log errors only. See <xref linkend="logging"/>.</para></listitem>
+            <term><option>--profile</option></term>
+            <listitem><para>Profiles build execution time and generates a report in the
+                <build_dir>/reports/profile directory.
+            </para></listitem>
         </varlistentry>
         <varlistentry>
-            <term><option>-r</option>, <option>--properties</option></term>
-            <listitem><para>Show list of all available project properties. See <xref linkend="sec:listing_properties"/>.</para></listitem>
+            <term><option>-q</option>, <option>--quiet</option></term>
+            <listitem><para>Log errors only. See <xref linkend="logging"/>.</para></listitem>
         </varlistentry>
         <varlistentry>
             <term><option>-s</option>, <option>--stacktrace</option></term>
@@ -128,11 +129,6 @@
             </para></listitem>
         </varlistentry>
         <varlistentry>
-            <term><option>-t</option>, <option>--tasks</option></term>
-            <listitem><para>Show list of all available tasks. See <xref linkend="sec:listing_tasks"/>.
-            </para></listitem>
-        </varlistentry>
-        <varlistentry>
             <term><option>-u</option>, <option>--no-search-upwards</option></term>
             <listitem><para>Don't search in parent directories for a <filename>settings.gradle</filename> file.
             </para></listitem>
@@ -148,6 +144,24 @@
             </para></listitem>
         </varlistentry>
     </variablelist>
-    
+
+    <para>The following options are deprecated and will be removed in a future version of Gradle:</para>
+    <variablelist>
+        <varlistentry>
+            <term><option>-n</option>, <option>--dependencies</option></term>
+            <listitem><para>(deprecated) Show list of all project dependencies. See <xref linkend="sec:listing_dependencies"/>.
+            </para></listitem>
+        </varlistentry>
+        <varlistentry>
+            <term><option>-r</option>, <option>--properties</option></term>
+            <listitem><para>(deprecated) Show list of all available project properties. See <xref linkend="sec:listing_properties"/>.</para></listitem>
+        </varlistentry>
+        <varlistentry>
+            <term><option>-t</option>, <option>--tasks</option></term>
+            <listitem><para>(deprecated) Show list of available tasks. See <xref linkend="sec:listing_tasks"/>.
+            </para></listitem>
+        </varlistentry>
+    </variablelist>
+
     <para>The same information is printed to the console when you execute <userinput>gradle -h</userinput>.</para>
 </appendix>
diff --git a/subprojects/gradle-docs/src/docs/userguide/commandLineTutorial.xml b/subprojects/gradle-docs/src/docs/userguide/commandLineTutorial.xml
index b7526b4..eef59cb 100644
--- a/subprojects/gradle-docs/src/docs/userguide/commandLineTutorial.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/commandLineTutorial.xml
@@ -24,7 +24,7 @@
             the command <userinput>gradle compile test</userinput> will execute the <literal>compile</literal> and
             <literal>test</literal> tasks. Gradle will execute the tasks in the order that they are listed on the
             command-line, and will also execute the dependencies for each task. Each task is executed once only,
-            regardless of why it is included in the build: whether it was specified on the command-line, or it a
+            regardless of how it came to be included in the build: whether it was specified on the command-line, or it a
             dependency of another task, or both. Let's look at an example.</para>
         <para>
             Below four tasks are defined. Both <literal>dist</literal> and <literal>test</literal> depend on the
@@ -65,7 +65,7 @@
             task name to uniquely identify the task. For example, in the sample build above, you can execute task
             <literal>dist</literal> by running <userinput>gradle d</userinput>:</para>
         <sample id="abbreviateTaskName" dir="userguide/tutorial/excludeTasks" title="Abbreviated task name">
-            <output args="d"/>
+            <output args="di"/>
         </sample>
         <para>You can also abbreviate each word in a camel case task name. For example, you can execute task <literal>compileTest</literal>
             by running <userinput>gradle compTest</userinput> or even <userinput>gradle cT</userinput></para>
@@ -92,62 +92,91 @@
 
     <section id="sec:obtaining_information_about_your_build">
         <title>Obtaining information about your build</title>
-        <para>Gradle provides several command-line options which show particular details of your build. This can be
+        <para>Gradle provides several built-in tasks which show particular details of your build. This can be
             useful for understanding the structure and dependencies of your build, and for debugging problems.
         </para>
-        <para>If a report option is used without additional parameters, the respective report is generated for the current project.
-           If you add append <literal>?</literal> to the option the report is generated for the current project and all its subprojects.
-           If you add append a <link linkend="sec:project_and_task_paths">project path</link> the report will be generated for the
-            specified project.
-        </para>
-        <para>In addition to the command-line options shown below, you can also use the
+        <para>In addition to the built-in tasks shown below, you can also use the
             <link linkend="project_reports_plugin">project report plugin</link> to add tasks to your project which will
             generate these reports.
         </para>
+        <section>
+            <title>Listing projects</title>
+            <para>Running <userinput>gradle projects</userinput> gives you a list of the sub-projects of the selected project,
+                displayed in a hierarchy. Here is an example:
+            </para>
+            <sample id="projectListReport" dir="userguide/tutorial/projectReports" title="Obtaining information about projects">
+                <output args="-q projects"/>
+            </sample>
+            <para>The report shows the description of each project, if specified. You can provide a description for a project
+                by setting the <literal>description</literal> property:
+            </para>
+            <sample id="projectListReport" dir="userguide/tutorial/projectReports" title="Providing a description for a project">
+                <sourcefile file="build.gradle" snippet="project-description"/>
+            </sample>
+        </section>
         <section id="sec:listing_tasks">
             <title>Listing tasks</title>
-            <para>Running <userinput>gradle --tasks</userinput> or <userinput>gradle -t</userinput>
-                gives you a list of the main tasks which make up the selected project. This report shows the default
-                tasks for the project, if any, and a description for each task. Below is an example of this report:
+            <para>Running <userinput>gradle tasks</userinput> gives you a list of the main tasks of the
+                selected project. This report shows the default tasks for the project, if any, and a description for
+                each task. Below is an example of this report:
             </para>
             <sample id="taskListReport" dir="userguide/tutorial/projectReports" title="Obtaining information about tasks">
-                <output args="-q -t"/>
+                <output args="-q tasks"/>
             </sample>
             <para>By default, this report shows only those tasks which have been assigned to a task group. You can do this
                 by setting the <literal>group</literal> property for the task. You can also set the <literal>description</literal>
-                property, to provide a description to be included in the report for the task.
+                property, to provide a description to be included in the report.
             </para>
             <sample id="taskListReport" dir="userguide/tutorial/projectReports" title="Changing the content of the task report">
                 <sourcefile file="build.gradle" snippet="add-task-to-report"/>
             </sample>
-            <para>You can obtain more information in the task listing by adding the <option>--all</option> option. With
+            <para>You can obtain more information in the task listing using the <option>--all</option> option. With
                 this option, the task report lists all tasks in the project, grouped by main task, and the dependencies
                 for each task. Here is an example:
             </para>
             <sample id="taskListAllReport" dir="userguide/tutorial/projectReports" title="Obtaining more information about tasks">
-                <output args="-q -t --all"/>
+                <output args="-q tasks --all"/>
             </sample>
         </section>
         <section id="sec:listing_dependencies">
             <title>Listing project dependencies</title>
-            <para id="para:commandline_dependency_report">Running <userinput>gradle --dependencies</userinput>
+            <para id="para:commandline_dependency_report">Running <userinput>gradle dependencies</userinput>
                 gives you a list of the dependencies of the selected project, broken down by configuration.  For each
                 configuration, the direct and transitive dependencies of that configuration are shown in a tree. Below
                 is an example of this report:
             </para>
             <sample id="dependencyListReport" dir="userguide/tutorial/projectReports" title="Obtaining information about dependencies">
-                <output args="-q --dependencies ?"/>
+                <output args="-q dependencies api:dependencies webapp:dependencies"/>
             </sample>
         </section>
         <section id="sec:listing_properties">
             <title>Listing project properties</title>
-            <para>Running <userinput>gradle --properties</userinput> gives you a list of the properties of the selected
+            <para>Running <userinput>gradle properties</userinput> gives you a list of the properties of the selected
                 project. This is a snippet from the output:
             </para>
             <sample id="propertyListReport" dir="userguide/tutorial/projectReports" title="Information about properties">
-                <output args="-q --properties :api" ignoreExtraLines="true"/>
+                <output args="-q api:properties" ignoreExtraLines="true"/>
             </sample>
         </section>
+        <section id="sec:profiling_build">
+            <title>Profiling a build</title>
+            <para>The <userinput>--profile</userinput> command line option will record some useful timing information
+            while your build is running and write a report to the <filename>build/reports/profile</filename> directory.
+            The report will be named using the time when the build was run.
+            </para>
+            <para>This report lists summary times and details for both the configuration phase and task execution.  The
+                times for configuration and task execution are sorted with the most expensive operations first.  The task
+                execution results also indicate if any tasks were skipped (and the reason) or if tasks that were not skipped
+                did no work.
+            </para>
+            <para>Builds which utilize a buildSrc directory will generate a second profile report for buildSrc in the
+                <filename>buildSrc/build</filename> directory.                
+            </para>
+            <imageobject>
+                <imagedata fileref="img/profile.png" width="903px" depth="477px"/>
+            </imageobject>
+
+        </section>
     </section>
 
     <section>
@@ -156,7 +185,7 @@
             command line, but you don't want the tasks to be executed. You can use the <option>-m</option> option for this.
             For example <userinput>gradle -m clean compile</userinput> shows you all tasks to be executed as
             part of the <literal>clean</literal> and <literal>compile</literal> tasks.
-            This is complementary to the <option>-t</option> option, which shows you the tasks which are available for
+            This is complementary to the <option>tasks</option> task, which shows you the tasks which are available for
             execution.
         </para>
     </section>
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/dsl.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/dsl.xml
new file mode 100644
index 0000000..f422088
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/dsl.xml
@@ -0,0 +1,36 @@
+<appendix id="dsl">
+    <title>Build script reference</title>
+    <note>
+        <para>This chapter is under construction.</para>
+    </note>
+    <para>This chapter describes the various types which make up the Gradle build DSL.</para>
+
+    <!--
+    -
+    - To add more types to this chapter, add them to one of the following tables. The contents of
+    - ${classname}.xml for each type listed below is added to this chapter.
+    -
+    -->
+
+    <table>
+        <title>Core types</title>
+        <tr>
+            <td>org.gradle.api.Project</td>
+        </tr>
+        <tr>
+            <td>org.gradle.api.tasks.SourceSet</td>
+        </tr>
+    </table>
+    <table>
+        <title>Task types</title>
+        <tr>
+            <td>org.gradle.plugins.idea.IdeaProject</td>
+        </tr>
+        <tr>
+            <td>org.gradle.api.tasks.bundling.Tar</td>
+        </tr>
+        <tr>
+            <td>org.gradle.api.tasks.bundling.Zip</td>
+        </tr>
+    </table>
+</appendix>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.DefaultTask.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.DefaultTask.xml
new file mode 100644
index 0000000..4097d29
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.DefaultTask.xml
@@ -0,0 +1,26 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.Project.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.Project.xml
new file mode 100644
index 0000000..f8334fb
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.Project.xml
@@ -0,0 +1,40 @@
+<section>
+    <para>The main API you use to interact with Gradle from your build file.</para>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>name</td>
+                <td>The name of the project.</td>
+            </tr>
+            <tr>
+                <td>path</td>
+                <td>The path of the project.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td><literal>file</literal></td>
+                <td><literal>File file(Object path)</literal></td>
+                <td>Resolves a file path relative to the project directory of this project.</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.internal.AbstractTask.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.internal.AbstractTask.xml
new file mode 100644
index 0000000..4097d29
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.internal.AbstractTask.xml
@@ -0,0 +1,26 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.internal.ConventionTask.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.internal.ConventionTask.xml
new file mode 100644
index 0000000..4097d29
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.internal.ConventionTask.xml
@@ -0,0 +1,26 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.plugins.BasePluginConvention.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.plugins.BasePluginConvention.xml
new file mode 100644
index 0000000..133efcd
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.plugins.BasePluginConvention.xml
@@ -0,0 +1,30 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>distsDir</td>
+                <td>The directory which distributions should be generated into.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.plugins.ReportingBasePluginConvention.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.plugins.ReportingBasePluginConvention.xml
new file mode 100644
index 0000000..e613b39
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.plugins.ReportingBasePluginConvention.xml
@@ -0,0 +1,30 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>reportsDirName</td>
+                <td>The name of the reports directory, relative to the project's build directory.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.AbstractCopyTask.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.AbstractCopyTask.xml
new file mode 100644
index 0000000..23d7304
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.AbstractCopyTask.xml
@@ -0,0 +1,39 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>includes</td>
+                <td>The set of include patterns.</td>
+            </tr>
+            <tr>
+                <td>excludes</td>
+                <td>The set of exclude patterns.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td><literal>from</literal></td>
+                <td><literal>AbstractCopyTask from(Object... sourcePaths)</literal></td>
+                <td>Specifies source files or directories to include.</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.GroovySourceSet.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.GroovySourceSet.xml
new file mode 100644
index 0000000..9dce1a6
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.GroovySourceSet.xml
@@ -0,0 +1,32 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>groovy</td>
+                <td>The source to be compiled by the Groovy compiler for this source set. Any Java source present in
+                    this set will be passed to the Groovy compiler for joint compilation.
+                </td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.SourceSet.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.SourceSet.xml
new file mode 100644
index 0000000..695de81
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.SourceSet.xml
@@ -0,0 +1,39 @@
+<section>
+    <para>Represents a logical group of Java source and resources.</para>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>name</td>
+                <td>The name of the source set.</td>
+            </tr>
+            <tr>
+                <td>compileClasspath</td>
+                <td>The classpath used to compile this source.</td>
+            </tr>
+            <tr>
+                <td>runtimeClasspath</td>
+                <td>The classpath used to execute this source.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.AbstractArchiveTask.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.AbstractArchiveTask.xml
new file mode 100644
index 0000000..df268b7
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.AbstractArchiveTask.xml
@@ -0,0 +1,35 @@
+<section>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>archiveName</td>
+                <td>The archive name. If the name has not been explicitly set, the pattern for the name is:
+                    <literal>[baseName]-[appendix]-[version]-[classifier].[extension]</literal>.</td>
+            </tr>
+            <tr>
+                <td>destinationDir</td>
+                <td>The directory where the archive is generated into.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.Tar.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.Tar.xml
new file mode 100644
index 0000000..80edd91
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.Tar.xml
@@ -0,0 +1,31 @@
+<section>
+    <para>Assembles a TAR archive.</para>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>compression</td>
+                <td>The compression to use for this archive.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.Zip.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.Zip.xml
new file mode 100644
index 0000000..8a9a63b
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.api.tasks.bundling.Zip.xml
@@ -0,0 +1,27 @@
+<section>
+    <para>Assembles a ZIP archive.</para>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.plugins.idea.IdeaProject.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.plugins.idea.IdeaProject.xml
new file mode 100644
index 0000000..421e73f
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/org.gradle.plugins.idea.IdeaProject.xml
@@ -0,0 +1,40 @@
+<section>
+    <para>Generates an IDEA project file.</para>
+    <section>
+        <title>Properties</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Type</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td>outputFile</td>
+                <td>The ipr file.</td>
+            </tr>
+            <tr>
+                <td>javaVersion</td>
+                <td>The java version used for defining the project sdk.</td>
+            </tr>
+        </table>
+    </section>
+    <section>
+        <title>Methods</title>
+        <table>
+            <thead>
+                <tr>
+                    <td>Name</td>
+                    <td>Signature</td>
+                    <td>Description</td>
+                </tr>
+            </thead>
+            <tr>
+                <td><literal>withXml</literal></td>
+                <td><literal>IdeaProject withXml(Closure closure)</literal></td>
+                <td>Adds a closure to be called when the ipr xml has been created.</td>
+            </tr>
+        </table>
+    </section>
+</section>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/dsl/plugins.xml b/subprojects/gradle-docs/src/docs/userguide/dsl/plugins.xml
new file mode 100644
index 0000000..9f17cbd
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/dsl/plugins.xml
@@ -0,0 +1,13 @@
+<plugins>
+    <plugin id="java" description="Java Plugin">
+        <extends targetClass="org.gradle.api.Project" extensionClass="org.gradle.api.plugins.BasePluginConvention"/>
+        <extends targetClass="org.gradle.api.Project" extensionClass="org.gradle.api.plugins.ReportingBasePluginConvention"/>
+        <!--<extends targetClass="org.gradle.api.Project" extensionClass="org.gradle.api.plugins.JavaPluginConvention"/>-->
+    </plugin>
+    <plugin id="groovy" description="Groovy Plugin">
+        <extends targetClass="org.gradle.api.tasks.SourceSet" extensionClass="org.gradle.api.tasks.GroovySourceSet"/>
+    </plugin>
+    <!--<plugin id="scala" description="Scala Plugin">-->
+        <!--<extends targetClass="org.gradle.api.tasks.SourceSet" extensionClass="org.gradle.api.tasks.ScalaSourceSet"/>-->
+    <!--</plugin>-->
+</plugins>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/eclipsePlugin.xml b/subprojects/gradle-docs/src/docs/userguide/eclipsePlugin.xml
index 0ef720f..98bac4e 100644
--- a/subprojects/gradle-docs/src/docs/userguide/eclipsePlugin.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/eclipsePlugin.xml
@@ -365,8 +365,8 @@
                 <td>
                     <literal><code>withXml { arg -> }</code></literal>
                 </td>
-                <td><code>groovy.util.Node</code></td>
-                <td><code>groovy.util.Node</code></td>
+                <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
                 <td><code>['org.eclipse.wst.common.component': groovy.util.Node, 'org.eclipse.wst.common.project.facet.core': groovy.util.Node]</code></td>
                 <td>The root node of the XML just before the XML is written to disk.</td>
             </tr>
diff --git a/subprojects/gradle-docs/src/docs/userguide/ideaPlugin.xml b/subprojects/gradle-docs/src/docs/userguide/ideaPlugin.xml
index 4ba1fe2..6c15537 100644
--- a/subprojects/gradle-docs/src/docs/userguide/ideaPlugin.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/ideaPlugin.xml
@@ -373,9 +373,9 @@
                 <td>
                     <literal><code>withXml { arg -> }</code></literal>
                 </td>
-                <td><code>groovy.util.Node</code></td>
-                <td><code>groovy.util.Node</code></td>
-                <td><code>groovy.util.Node</code></td>
+                <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
+                <td><apilink class="org.gradle.api.artifacts.maven.XmlProvider"/></td>
                 <td>The root node of the XML just before the XML is written to disk.</td>
             </tr>
         </table>
diff --git a/subprojects/gradle-docs/src/docs/userguide/img/profile.png b/subprojects/gradle-docs/src/docs/userguide/img/profile.png
new file mode 100644
index 0000000..0f4e2db
Binary files /dev/null and b/subprojects/gradle-docs/src/docs/userguide/img/profile.png differ
diff --git a/subprojects/gradle-docs/src/docs/userguide/installation.xml b/subprojects/gradle-docs/src/docs/userguide/installation.xml
index 14d3263..896f89a 100644
--- a/subprojects/gradle-docs/src/docs/userguide/installation.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/installation.xml
@@ -20,9 +20,8 @@
 <section>
     <title>Prerequisites</title>
 
-    <para>Gradle requires a Java JDK to be installed. Gradle ships with its own Groovy library, therefore no Groovy
-        needs to be installed. Any existing Groovy installation is ignored by Gradle. The standard Gradle distribution
-        requires a JDK 1.5 or higher. We also provide a distinct JDK 1.4 compatible distribution.
+    <para>Gradle requires a Java JDK to be installed. Gradle requires a JDK 1.5 or higher. Gradle ships with its own
+        Groovy library, therefore no Groovy needs to be installed. Any existing Groovy installation is ignored by Gradle.
     </para>
 
     <para>Gradle uses whichever JDK it finds in your path (to check, use <userinput>java -version</userinput>).
@@ -51,8 +50,8 @@
         </para>
     </listitem>
     <listitem>
-        <para>The binary sources (If you want to build Gradle you need to download the source distribution or checkout
-            the sources from the source repository).
+        <para>The binary sources. This is for reference only. If you want to build Gradle you need to download the source distribution
+            or checkout the sources from the source repository. See the <ulink url="website:build.html">Gradle web site</ulink> for details).
         </para>
     </listitem>
 </itemizedlist>
@@ -84,17 +83,15 @@ some zip front ends for Mac OS X don't restore the file permissions properly.
 
 <screen>
 ------------------------------------------------------------
-Gradle 0.9-rc-1
+Gradle 0.9-rc-2
 ------------------------------------------------------------
 
-Gradle buildtime: Tuesday, 3 August 2010 6:59:23 PM EST
-Groovy: 1.7.3
+Gradle build time: Wednesday, 27 October 2010 5:05:52 PM EST
+Groovy: 1.7.5
 Ant: Apache Ant version 1.8.1 compiled on April 30 2010
-Ivy: 2.2.0-rc1
-Java: 1.6.0_20
-JVM: 16.3-b01
-JVM Vendor: Sun Microsystems Inc.
-OS Name: Linux
+Ivy: 2.2.0
+JVM: 1.6.0_20 (Sun Microsystems Inc. 16.3-b01)
+OS: Linux 2.6.35-22-generic amd64
 </screen>
 
 </section>
@@ -116,7 +113,7 @@ of the <command>gradle</command> or <command>gradlew</command> script.
 
 <para>You might check the user guide at <filename><replaceable>GRADLE_HOME</replaceable>/docs/userguide/userguide.html</filename>.
 It is also available on the <ulink url="website:userguide.html">Gradle web site</ulink>.
-Typing <command>gradle -h</command> prints the command line help. Typing <command>gradle -t</command> shows all the
+Typing <command>gradle help</command> prints the command line help. Typing <command>gradle tasks</command> shows all the
 tasks of a Gradle build.
 </para>
 
diff --git a/subprojects/gradle-docs/src/docs/userguide/javaTutorial.xml b/subprojects/gradle-docs/src/docs/userguide/javaTutorial.xml
index 4b93741..9527983 100644
--- a/subprojects/gradle-docs/src/docs/userguide/javaTutorial.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/javaTutorial.xml
@@ -57,7 +57,7 @@
             a number of tasks to your project.</para>
         <tip>
             <title>What tasks are available?</title>
-            <para>You can use <userinput>gradle -t</userinput> to list the tasks of a project. This will let you see
+            <para>You can use <userinput>gradle tasks</userinput> to list the tasks of a project. This will let you see
             the tasks that the Java plugin has added to your project.
         </para></tip>
 
@@ -141,7 +141,7 @@
             </sample>
             <tip>
                 <title>What properties are available?</title>
-                <para>You can use <userinput>gradle -r</userinput> to list the properties of a project. This will allow
+                <para>You can use <userinput>gradle properties</userinput> to list the properties of a project. This will allow
                 you to see the properties added by the Java plugin, and their default values.</para></tip>
             <para>The tasks which the Java plugin adds are regular tasks, exactly the same as if they were declared in
                 the build file. This means you can use any of the mechanisms shown in earlier chapters to customise
diff --git a/subprojects/gradle-docs/src/docs/userguide/logging.xml b/subprojects/gradle-docs/src/docs/userguide/logging.xml
index 75dc1d9..62b9276 100644
--- a/subprojects/gradle-docs/src/docs/userguide/logging.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/logging.xml
@@ -129,7 +129,7 @@
         <title>Writing your own log messages</title>
         <para>A simple option for logging in your build file is to write messages to standard output. Gradle redirects
             anything written to standard output to it's logging system at the <literal>QUIET</literal> log level.</para>
-        <sample id="logging_to_stdout" dir="logging/project1" title="Using stdout to write log messages">
+        <sample id="logging_to_stdout" dir="userguide/tutorial/logging" title="Using stdout to write log messages">
             <sourcefile file="build.gradle" snippet="use-println"/>
         </sample>
         <para>Gradle also provides a <literal>logger</literal> property to a build script, which is an instance of
@@ -137,14 +137,14 @@
             <classname>Logger</classname> interface and adds a few Gradle specific methods to it. Below is an example
             of how this is used in the build script:
         </para>
-        <sample id="logging_ex" dir="logging/project1" title="Writing your own log messages">
+        <sample id="logging_ex" dir="userguide/tutorial/logging" title="Writing your own log messages">
             <sourcefile file="build.gradle" snippet="use-logger"/>
         </sample>
         <para>You can also hook into Gradle's logging system from within other classes used in the build (classes from
             the <filename>buildSrc</filename> directory for example). Simply use an SLF4J logger. You can use this
             logger the same way as you use the provided logger in the build script.
         </para>
-        <sample id="logging_with_slf4j" dir="logging/project1" title="Using SLF4J to write log messages">
+        <sample id="logging_with_slf4j" dir="userguide/tutorial/logging" title="Using SLF4J to write log messages">
             <sourcefile file="build.gradle" snippet="use-slf4j"/>
         </sample>
     </section>
@@ -162,14 +162,15 @@
             level. This behavior is configurable. The project object provides a
             <apilink class="org.gradle.api.logging.LoggingManager"/>, which allows you to change the log levels that
             standard out or error are redirected to when your build script is evaluated.</para>
-        <sample id="project_stdout_capture" dir="logging/project1" title="Configuring standard output capture">
+        <sample id="project_stdout_capture" dir="userguide/tutorial/logging" title="Configuring standard output capture">
             <sourcefile file="build.gradle" snippet="capture-stdout"/>
         </sample>
         <para>
             To change the log level for standard out or error during task execution, tasks also provide a <apilink class="org.gradle.api.logging.LoggingManager"/>.
         </para>
-        <sample id="task_stdout_capture" dir="logging/project1" title="Configuring standard output capture for a task">
+        <sample id="task_stdout_capture" dir="userguide/tutorial/logging" title="Configuring standard output capture for a task">
             <sourcefile file="build.gradle" snippet="task-capture-stdout"/>
+            <test args="logInfo"/>
         </sample>
         <para>Gradle also provides integration with the Java Util Logging, Jakarta Commons Logging and Log4j logging
             toolkits. Any log messages which your build classes write using these logging toolkits will be redirected to
diff --git a/subprojects/gradle-docs/src/docs/userguide/mavenPlugin.xml b/subprojects/gradle-docs/src/docs/userguide/mavenPlugin.xml
index 5ddb1d7..90321a0 100644
--- a/subprojects/gradle-docs/src/docs/userguide/mavenPlugin.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/mavenPlugin.xml
@@ -343,7 +343,7 @@
                 </sample>
                 <para>You need to declare a filter for each artifact you want to publish. This filter defines a boolean expression for
                     which Gradle artifact it accepts. Each filter has a POM associated with it which you can configure.
-                    To learn more about this have a look at <apilink class="org.gradle.api.artifacts.maven.GroovyPomFilterContainer"/>
+                    To learn more about this have a look at <apilink class="org.gradle.api.artifacts.maven.PomFilterContainer"/>
                     and its associated classes.
                 </para>
             </section>
diff --git a/subprojects/gradle-docs/src/docs/userguide/projectReports.xml b/subprojects/gradle-docs/src/docs/userguide/projectReports.xml
index a2046f3..4dd42ea 100644
--- a/subprojects/gradle-docs/src/docs/userguide/projectReports.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/projectReports.xml
@@ -24,7 +24,8 @@
 
     <para>The Project report plugin adds some tasks to your project which generate reports containing useful
         information about your build. Those tasks generate exactly the same content as the command line reports triggered
-        by <literal>-t</literal>, <literal>-n</literal>, <literal>-r</literal> (see <xref linkend="sec:obtaining_information_about_your_build"/>).
+        by <userinput>gradle tasks</userinput>, <userinput>gradle dependencies</userinput> and
+        <userinput>gradle properties</userinput> (see<xref linkend="sec:obtaining_information_about_your_build"/>).
         In contrast to the command line reports, the report plugin generates the reports into a file. There is also an
         aggregating task that depends on all report tasks added by the plugin.
     </para>
diff --git a/subprojects/gradle-docs/src/docs/userguide/standardTasks.xml b/subprojects/gradle-docs/src/docs/userguide/standardTasks.xml
new file mode 100644
index 0000000..569b081
--- /dev/null
+++ b/subprojects/gradle-docs/src/docs/userguide/standardTasks.xml
@@ -0,0 +1,155 @@
+<chapter id="standard_tasks">
+    <title>Standard Gradle tasks</title>
+    <para>There are a number of tasks included in the Gradle distribution. These are listed below.
+    </para>
+    <table>
+        <title>Standard tasks</title>
+        <thead>
+            <tr>
+                <td>Task class</td>
+                <td>Description</td>
+            </tr>
+        </thead>
+        <tr>
+            <td><apilink class='org.gradle.api.plugins.antlr.AntlrTask'/></td>
+            <td>Generates parsers from Antlr grammars.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.plugins.quality.Checkstyle'/></td>
+            <td>Runs Checkstyle against some source files.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.plugins.quality.CodeNarc'/></td>
+            <td>Runs CodeNarc against some source files.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.compile.Compile'/></td>
+            <td>Compiles Java source files.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.Copy'/></td>
+            <td>Copies files into a destination directory. See <xref linkend="sec:copying_files"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.Delete'/></td>
+            <td>Deletes files or directories.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.Directory' lang='groovy'/></td>
+            <td>Creates a directory. See <xref linkend="sec:directory_creation"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.diagnostics.DependencyReportTask'/></td>
+            <td>Displays the dependency tree for a project.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.plugins.eclipse.EclipseClasspath' lang="groovy"/></td>
+            <td>Generates an Eclipse .classpath file.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.plugins.eclipse.EclipseProject' lang="groovy"/></td>
+            <td>Generates an Eclipse .project file.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.plugins.eclipse.EclipseWtp' lang="groovy"/></td>
+            <td>Generates Eclipse configuration files for Eclipse WTP.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.Exec'/></td>
+            <td>Executes a command line process.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.GradleBuild'/></td>
+            <td>Executes a Gradle build. See <xref linkend="sec:external_build"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.compile.GroovyCompile'/></td>
+            <td>Compiles Groovy and Java source files.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.javadoc.Groovydoc'/></td>
+            <td>Generates HTML API documentation for Groovy and Java classes.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.plugins.idea.IdeaModule' lang="groovy"/></td>
+            <td>Generates an IDEA module file.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.plugins.idea.IdeaProject' lang="groovy"/></td>
+            <td>Generates an IDEA project file.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.plugins.idea.IdeaWorkspace' lang="groovy"/></td>
+            <td>Generates an IDEA workspace file.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.bundling.Jar' lang='groovy'/></td>
+            <td>Assembles a JAR archive. See <xref linkend="sec:archives"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.javadoc.Javadoc'/></td>
+            <td>Generates HTML API documentation for Java classes.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.JavaExec'/></td>
+            <td>Executes a Java application.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.plugins.jetty.JettyRun'/></td>
+            <td>Deploys an exploded web application to an embedded Jetty web container.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.plugins.jetty.JettyRunWar'/></td>
+            <td>Deploys a WAR to an embedded Jetty web container.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.plugins.jetty.JettyStop'/></td>
+            <td>Stops the embedded Jetty web container.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.diagnostics.PropertyReportTask'/></td>
+            <td>Displays the properties of a project.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.scala.ScalaCompile'/></td>
+            <td>Compiles Scala and Java source files.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.scala.ScalaDoc'/></td>
+            <td>Generates HTML API documentation for Scala source files.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.Sync'/></td>
+            <td>Synchronises the contents of a destination directory with some source. See <xref linkend="sec:sync_task"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.bundling.Tar'/></td>
+            <td>Assembles a TAR archive. See <xref linkend="sec:archives"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.diagnostics.TaskReportTask'/></td>
+            <td>Displays a list of tasks in the project.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.testing.Test'/></td>
+            <td>Executes tests.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.Upload'/></td>
+            <td>Uploads the artifacts of a configuration to a set of repositories.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.bundling.War' lang='groovy'/></td>
+            <td>Assembles a WAR archive. See <xref linkend="sec:archives"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.wrapper.Wrapper'/></td>
+            <td>Generates scripts (for *nix and windows) which enable you to build your project with Gradle, without
+                having to install Gradle. See <xref linkend="gradle_wrapper"/>.</td>
+        </tr>
+        <tr>
+            <td><apilink class='org.gradle.api.tasks.bundling.Zip'/></td>
+            <td>Assembles a ZIP archive. See <xref linkend="sec:archives"/>.</td>
+        </tr>
+    </table>
+</chapter>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/docs/userguide/tasks.xml b/subprojects/gradle-docs/src/docs/userguide/tasks.xml
index e68bfb5..07c499f 100644
--- a/subprojects/gradle-docs/src/docs/userguide/tasks.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/tasks.xml
@@ -13,7 +13,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<chapter id='more_about_tasks' xmlns:xi="http://www.w3.org/2001/XInclude">
+<chapter id='more_about_tasks'>
     <title>More about Tasks</title>
     <para>In the introductory tutorial (<xref linkend='tutorial_using_tasks'/>) you have learned how to
         create simple tasks. You have also learned how to add additional behavior to these tasks later on. And you have
@@ -154,7 +154,7 @@
     <section>
         <title>Adding a description to a task</title>
         <para>You can add a description to your task. This description is for example displayed when executing
-            <code>gradle -t</code>.
+            <userinput>gradle tasks</userinput>.
         </para>
         <sample id="describeTask" dir="userguide/tasks/defineAndConfigure" title="Adding a description to a task">
             <sourcefile file="build.gradle"/>
@@ -227,18 +227,75 @@
         </sample>
         </section>
     </section>
-    
+
+    <section>
+        <title>Skipping tasks that are up-to-date</title>
+        <para>If you are using one of the tasks that come with Gradle, such as a task added by the Java plugin,
+            you might have noticed that Gradle will skip tasks that are up-to-date. This behaviour is also available
+            for your tasks, not just for built-in tasks.
+        </para>
+        <section>
+            <title>Declaring a task's inputs and outputs</title>
+            <para>
+                Let's have a look at an example. Here our task generates several output files from a source XML file. Let's
+                run it a couple of times.
+            </para>
+            <sample id="generatorTask" dir="userguide/tasks/incrementalBuild/noInputsAndOutputs" title="A generator task">
+                <sourcefile file="build.gradle"/>
+                <output args="transform" ignoreExtraLines="true"/>
+                <output args="transform" ignoreExtraLines="true"/>
+            </sample>
+            <para>Notice that Gradle executes this task a second time, and does not skip the task even though nothing has
+                changed. Our example task was defined using an action closure. Gradle has no idea what the closure does and
+                cannot automatically figure out whether the task is up-to-date or not. To use Gradle's up-to-date checking,
+                you need to declare the inputs and outputs of the task.
+            </para>
+            <para>Each task has an <literal>inputs</literal> and <literal>outputs</literal> property, which you use to
+                declare the inputs and outputs of the task. Below, we have changed our example to declare that it takes
+                the source XML file as an input and produces output to a destination directory. Let's run it a couple
+                of times.
+            </para>
+            <sample id="incrementalTask" dir="userguide/tasks/incrementalBuild/inputsAndOutputs" title="Declaring the inputs and outputs of a task">
+                <sourcefile file="build.gradle"/>
+                <output args="transform" outputFile="generatorTask.out" ignoreExtraLines="true"/>
+                <output args="transform" ignoreExtraLines="true"/>
+            </sample>
+            <para>Now, Gradle knows which files to check to determine whether the task is up-to-date or not.</para>
+
+            <para>The task's <literal>inputs</literal> property is of type <apilink class="org.gradle.api.tasks.TaskInputs"/>.
+                The task's <literal>outputs</literal> property is of type <apilink class="org.gradle.api.tasks.TaskOutputs"/>.
+            </para>
+        </section>
+        <section>
+            <title>How does it work?</title>
+            <para>
+                Before a task is executed for the first time, Gradle takes a snapshot of the inputs. This snapshot contains
+                the set of input files and a hash of the contents of each file. Gradle then executes the task. If the
+                task completes successfully, Gradle takes a snapshot of the outputs. This snapshot contains the set of
+                output files and a hash of the contents of each file. Gradle takes note of any files created, changed or
+                deleted in the output directories of the task. Gradle persists both snapshots for next time the task
+                is executed.
+            </para>
+            <para>
+                Each time after that, before the task is executed, Gradle takes a new snapshot of the inputs and outputs.
+                If the new snapshots are the same as the previous snapshots, Gradle assumes that the outputs are up to
+                date and skips the task. If they are not the same, Gradle executes the task. Gradle persists both snapshots
+                for next time the task is executed.
+            </para>
+        </section>
+    </section>
+
     <section>
         <title>Task rules</title>
         <para>Sometimes you want to have a task which behavior depends on a large or infinite number value range
-            of parameters. A very nice and expressive way to provide such tasks are task rules: 
+            of parameters. A very nice and expressive way to provide such tasks are task rules:
         </para>
         <sample id="taskRule" dir="userguide/tasks/addRules" title="Task rule">
             <sourcefile file="build.gradle" snippet="task-rule"/>
             <output args="-q pingServer1"/>
         </sample>
-        <para>The String parameter is used as a description for the rule. This description is shown when doing
-            for example <code>gradle -t</code>.
+        <para>The String parameter is used as a description for the rule. This description is shown when running
+            for example <userinput>gradle tasks</userinput>.
         </para>
         <para>Rules not just work for calling tasks from the command line. You can also create dependsOn relations
             on rule based tasks:
diff --git a/subprojects/gradle-docs/src/docs/userguide/userguide.xml b/subprojects/gradle-docs/src/docs/userguide/userguide.xml
index e75ece4..7eb3fa7 100644
--- a/subprojects/gradle-docs/src/docs/userguide/userguide.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/userguide.xml
@@ -55,6 +55,7 @@
     <xi:include href='ant.xml'/>
     <xi:include href='plugins.xml'/>
     <xi:include href='standardPlugins.xml'/>
+    <xi:include href='standardTasks.xml'/>
     <xi:include href='javaPlugin.xml'/>
     <xi:include href='groovyPlugin.xml'/>
     <xi:include href='scalaPlugin.xml'/>
@@ -79,6 +80,8 @@
     <xi:include href='gradleWrapper.xml'/>
     <xi:include href='embedding.xml'/>
     <!-- this is generated -->
+    <xi:include href='../../../build/src/docbook/dsl.xml'/>
+    <!-- this is generated -->
     <xi:include href='../../../build/src/docbook/samplesList.xml'/>
     <xi:include href='potentialTraps.xml'/>
     <xi:include href='commandLine.xml'/>
diff --git a/subprojects/gradle-docs/src/docs/userguide/workingWithFiles.xml b/subprojects/gradle-docs/src/docs/userguide/workingWithFiles.xml
index 5420fd4..82d81fb 100644
--- a/subprojects/gradle-docs/src/docs/userguide/workingWithFiles.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/workingWithFiles.xml
@@ -224,12 +224,19 @@
         </section>
     </section>
 
-    <section>
+    <section id="sec:sync_task">
         <title>Using the <literal>Sync</literal> task</title>
         <para>The <apilink class="org.gradle.api.tasks.Sync"/> task extends the <literal>Copy</literal> task. When it
             executes, it copies the source files into the destination directory, and then removes any files from the
-            destination directory which it did not copy.
+            destination directory which it did not copy. This can be useful for doing things such as installing your
+            application, creating an exploded copy of your archives, or maintaining a copy of the project's dependencies.
         </para>
+        <para>Here is an example which maintains a copy of the project's runtime dependencies in the <filename>build/libs</filename>
+            directory.</para>
+        <sample id="syncDependencies" dir="userguide/files/sync" title="Using the Sync task to copy dependencies">
+            <sourcefile file="build.gradle" snippet="copy-dependencies"/>
+            <test args="libs"/>
+        </sample>
     </section>
 
     <section id="sec:archives">
diff --git a/subprojects/gradle-docs/src/docs/userguide/writingBuildScripts.xml b/subprojects/gradle-docs/src/docs/userguide/writingBuildScripts.xml
index 24127ff..6d6f960 100644
--- a/subprojects/gradle-docs/src/docs/userguide/writingBuildScripts.xml
+++ b/subprojects/gradle-docs/src/docs/userguide/writingBuildScripts.xml
@@ -89,6 +89,11 @@
                     <td>The absolute path of the project.</td>
                 </tr>
                 <tr>
+                    <td><literal>description</literal></td>
+                    <td><classname>String</classname></td>
+                    <td>A description for the project.</td>
+                </tr>
+                <tr>
                     <td><literal>buildFile</literal></td>
                     <td><classname>File</classname></td>
                     <td>The build script.</td>
diff --git a/subprojects/gradle-docs/src/samples/codeQuality/build.gradle b/subprojects/gradle-docs/src/samples/codeQuality/build.gradle
index e69f10c..7a3f852 100644
--- a/subprojects/gradle-docs/src/samples/codeQuality/build.gradle
+++ b/subprojects/gradle-docs/src/samples/codeQuality/build.gradle
@@ -8,6 +8,6 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.0'
+    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.5'
     testCompile group: 'junit', name: 'junit', version: '4.7'
 }
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/groovy/customizedLayout/build.gradle b/subprojects/gradle-docs/src/samples/groovy/customizedLayout/build.gradle
index ef318dc..8296c8a 100644
--- a/subprojects/gradle-docs/src/samples/groovy/customizedLayout/build.gradle
+++ b/subprojects/gradle-docs/src/samples/groovy/customizedLayout/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.0'
+    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.5'
     testCompile group: 'junit', name: 'junit', version: '4.7'
 }
 
diff --git a/subprojects/gradle-docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle b/subprojects/gradle-docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
index aece9bb..0d219f9 100644
--- a/subprojects/gradle-docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
+++ b/subprojects/gradle-docs/src/samples/groovy/mixedJavaAndGroovy/build.gradle
@@ -6,6 +6,6 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.0'
+    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.5'
     testCompile group: 'junit', name: 'junit', version: '4.7'
 }
diff --git a/subprojects/gradle-docs/src/samples/groovy/multiproject/groovycDetector/build.gradle b/subprojects/gradle-docs/src/samples/groovy/multiproject/groovycDetector/build.gradle
index a2baec5..5e62afb 100644
--- a/subprojects/gradle-docs/src/samples/groovy/multiproject/groovycDetector/build.gradle
+++ b/subprojects/gradle-docs/src/samples/groovy/multiproject/groovycDetector/build.gradle
@@ -3,5 +3,5 @@ apply plugin: 'java'
 version = 'SNAPSHOT'
 
 dependencies {
-    compile 'org.codehaus.groovy:groovy-all:1.6.0'
+    compile 'org.codehaus.groovy:groovy-all:1.6.7'
 }
diff --git a/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/build.gradle b/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/build.gradle
index a3086c8..e61eb68 100644
--- a/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/build.gradle
+++ b/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/build.gradle
@@ -4,7 +4,7 @@ group = 'org.gradle'
 version = '1.0'
 
 dependencies {
-    groovy 'org.codehaus.groovy:groovy-all:1.7.0'
+    groovy 'org.codehaus.groovy:groovy-all:1.7.5'
     compile project(':groovycDetector')
     testCompile 'junit:junit:4.7'
 }
diff --git a/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy b/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
index f52b51c..92be75a 100644
--- a/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
+++ b/subprojects/gradle-docs/src/samples/groovy/multiproject/testproject/src/test/groovy/org/gradle/VersionTest.groovy
@@ -7,7 +7,7 @@ class GroovycVersionTest {
   def groovycVersion
 
   @Test
-  void versionShouldBe1_7_0() {
-    assertEquals("1.7.0", groovycVersion)
+  void versionShouldBe1_7_5() {
+    assertEquals("1.7.5", groovycVersion)
   }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/groovy/quickstart/build.gradle b/subprojects/gradle-docs/src/samples/groovy/quickstart/build.gradle
index a257cea..5277b60 100644
--- a/subprojects/gradle-docs/src/samples/groovy/quickstart/build.gradle
+++ b/subprojects/gradle-docs/src/samples/groovy/quickstart/build.gradle
@@ -9,7 +9,7 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.0'
+    groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.5'
 // END SNIPPET groovy-dependency
     testCompile group: 'junit', name: 'junit', version: '4.7'
 // START SNIPPET groovy-dependency
diff --git a/subprojects/gradle-docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy b/subprojects/gradle-docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
index 9414383..b698368 100644
--- a/subprojects/gradle-docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/gradle-docs/src/samples/groovy/quickstart/src/test/groovy/org/gradle/PersonTest.groovy
@@ -16,7 +16,7 @@ class PersonTest {
     }
 
     @Test public void usingCorrectVersionOfGroovy() {
-        assertEquals('1.7.0', InvokerHelper.version)
+        assertEquals('1.7.5', InvokerHelper.version)
     }
     
     @Test public void testResourcesAreAvailable() {
diff --git a/subprojects/gradle-docs/src/samples/logging/project1/build.gradle b/subprojects/gradle-docs/src/samples/logging/project1/build.gradle
deleted file mode 100644
index 790dea5..0000000
--- a/subprojects/gradle-docs/src/samples/logging/project1/build.gradle
+++ /dev/null
@@ -1,75 +0,0 @@
-// START SNIPPET use-logger
-logger.quiet('An info log message which is always logged.')
-logger.error('An error log message.')
-logger.warn('A warning log message.')
-logger.lifecycle('A lifecycle info log message.')
-logger.info('An info log message.')
-logger.debug('A debug log message.')
-logger.trace('A trace log message.')
-// END SNIPPET use-logger
-
-// START SNIPPET use-println
-println 'A message which is logged at QUIET level'
-// END SNIPPET use-println
-
-// START SNIPPET capture-stdout
-logging.captureStandardOutput LogLevel.INFO
-println 'A message which is logged at INFO level'
-// END SNIPPET capture-stdout
-
-System.err.println 'An error message which is logged at ERROR level'
-logging.captureStandardError LogLevel.LIFECYCLE
-System.err.println 'An error message which is logged at LIFECYCLE level'
-
-task logLifecycle {
-    logging.captureStandardOutput LogLevel.LIFECYCLE
-    logging.captureStandardError LogLevel.WARN
-    doFirst {
-        println('A task message which is logged at LIFECYCLE level')
-        System.err.println('A task error message which is logged at WARN level')
-    }
-}
-
-// START SNIPPET task-capture-stdout
-task logInfo {
-    logging.captureStandardOutput LogLevel.INFO
-    doFirst {
-        println 'A task message which is logged at INFO level'
-    }
-}
-// END SNIPPET task-capture-stdout
-
-task nestedBuildLog << {
-    def startParam = project.gradle.startParameter.newBuild()
-    startParam.currentDir = rootProject.file('nestedBuild')
-    startParam.taskNames = ['log']
-    GradleLauncher.newInstance(startParam).run().rethrowFailure()
-}
-
-task log(dependsOn: [logInfo, logLifecycle, nestedBuildLog]) << {
-    println('A task message which is logged at QUIET level')
-}
-
-// warn is the default log level for echo
-ant.echo('A warn message logged from Ant')
-ant.echo('An error message logged from Ant', level: org.apache.tools.ant.types.LogLevel.ERR)
-ant.echo('An info message logged from Ant', level: org.apache.tools.ant.types.LogLevel.INFO)
-ant.echo('A debug message logged from Ant', level: org.apache.tools.ant.types.LogLevel.DEBUG)
-
-// START SNIPPET use-slf4j
-org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger('some-logger')
-slf4jLogger.info('An info log message logged using SLF4j')
-// END SNIPPET use-slf4j
-
-org.apache.commons.logging.Log jclLogger = org.apache.commons.logging.LogFactory.getLog('some-logger')
-jclLogger.info('An info log message logged using JCL')
-
-org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger('some-logger')
-log4jLogger.info('An info log message logged using Log4j')
-
-java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger('some-logger')
-julLogger.severe('A severe log message logged using JUL')
-julLogger.warning('A warning log message logged using JUL')
-julLogger.info('An info log message logged using JUL')
-julLogger.config('A config log message logged using JUL')
-julLogger.fine('A fine log message logged using JUL')
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/osgi/build.gradle b/subprojects/gradle-docs/src/samples/osgi/build.gradle
index 35fba11..ae44005 100644
--- a/subprojects/gradle-docs/src/samples/osgi/build.gradle
+++ b/subprojects/gradle-docs/src/samples/osgi/build.gradle
@@ -12,7 +12,7 @@ repositories {
 }
 
 dependencies {
-    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.0'
+    groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.5'
     compile group: 'org.eclipse', name: 'osgi', version: '3.4.3.R34x_v20081215-1030'
 }
 
diff --git a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/build.gradle b/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/build.gradle
deleted file mode 100644
index 5592f0b..0000000
--- a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-failing/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-apply plugin: 'groovy'
-
-sourceCompatibility=1.5
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-	groovy "org.codehaus.groovy:groovy-all:1.7.0"
-
-    testCompile 'org.testng:testng:5.8:jdk15'
-}
-
-test {
-   useTestNG() 
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/build.gradle b/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/build.gradle
deleted file mode 100644
index 5592f0b..0000000
--- a/subprojects/gradle-docs/src/samples/testng/groovy-jdk15-passing/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-apply plugin: 'groovy'
-
-sourceCompatibility=1.5
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-	groovy "org.codehaus.groovy:groovy-all:1.7.0"
-
-    testCompile 'org.testng:testng:5.8:jdk15'
-}
-
-test {
-   useTestNG() 
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/build.gradle b/subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/build.gradle
deleted file mode 100644
index 483abf0..0000000
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk14-failing/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-apply plugin: 'java'
-
-sourceCompatibility=1.4
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'org.testng:testng:5.8:jdk14'
-}
-
-test {
-    useTestNG()
-    scanForTestClasses = false
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk14-passing/build.gradle b/subprojects/gradle-docs/src/samples/testng/java-jdk14-passing/build.gradle
index 483abf0..0bc9c4e 100644
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk14-passing/build.gradle
+++ b/subprojects/gradle-docs/src/samples/testng/java-jdk14-passing/build.gradle
@@ -7,7 +7,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'org.testng:testng:5.8:jdk14'
+    testCompile 'org.testng:testng:5.11:jdk14'
 }
 
 test {
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/build.gradle b/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/build.gradle
deleted file mode 100644
index 2647d76..0000000
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk15-failing/build.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-apply plugin: 'java'
-
-sourceCompatibility=1.5
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'org.testng:testng:5.8:jdk15'
-}
-
-test {
-   useTestNG()
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/build.gradle b/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/build.gradle
deleted file mode 100644
index b7a1abe..0000000
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-apply plugin: 'java'
-
-sourceCompatibility=1.5
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testCompile 'org.testng:testng:5.8:jdk15'
-}
-
-test {
-    useTestNG() 
-	disableTestReport()
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/src/main/java/org/gradle/Ok.java b/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/src/main/java/org/gradle/Ok.java
deleted file mode 100644
index 2748554..0000000
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/src/main/java/org/gradle/Ok.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.gradle;
-public class Ok{
- String test = "dummy";
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/src/test/java/org/gradle/OkTest.java b/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/src/test/java/org/gradle/OkTest.java
deleted file mode 100644
index 53aa719..0000000
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing-no-report/src/test/java/org/gradle/OkTest.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.gradle;
-public class OkTest {
-   @org.testng.annotations.Test
-   public void passingTest() { }
-}
diff --git a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing/build.gradle b/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing/build.gradle
index f5493ed..75ef980 100644
--- a/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing/build.gradle
+++ b/subprojects/gradle-docs/src/samples/testng/java-jdk15-passing/build.gradle
@@ -7,7 +7,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'org.testng:testng:5.8:jdk15'
+    testCompile 'org.testng:testng:5.13.1'
 }
 
 test {
diff --git a/subprojects/gradle-docs/src/samples/testng/suitexmlbuilder/build.gradle b/subprojects/gradle-docs/src/samples/testng/suitexmlbuilder/build.gradle
index f4c07b0..28e8dbd 100644
--- a/subprojects/gradle-docs/src/samples/testng/suitexmlbuilder/build.gradle
+++ b/subprojects/gradle-docs/src/samples/testng/suitexmlbuilder/build.gradle
@@ -5,7 +5,7 @@ repositories {
 }
 
 dependencies {
-    testCompile 'org.testng:testng:5.8:jdk15'
+    testCompile 'org.testng:testng:5.13.1'
 }
 
 test {
diff --git a/subprojects/gradle-docs/src/samples/userguide/artifacts/externalDependencies/build.gradle b/subprojects/gradle-docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
index 0b1440b..1bbe7d4 100644
--- a/subprojects/gradle-docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
+++ b/subprojects/gradle-docs/src/samples/userguide/artifacts/externalDependencies/build.gradle
@@ -63,7 +63,7 @@ dependencies {
 
 //START SNIPPET client-modules
 dependencies {
-    runtime module("org.codehaus.groovy:groovy-all:1.7.0") {
+    runtime module("org.codehaus.groovy:groovy-all:1.7.5") {
         dependency("commons-cli:commons-cli:1.0") {
             transitive = false
         }
@@ -82,7 +82,7 @@ dependencies {
 //END SNIPPET file-dependencies
 
 //START SNIPPET list-grouping
-List groovy = ["org.codehaus.groovy:groovy-all:1.7.0 at jar",
+List groovy = ["org.codehaus.groovy:groovy-all:1.7.5 at jar",
                "commons-cli:commons-cli:1.0 at jar",
                "org.apache.ant:ant:1.7.0 at jar"]
 List hibernate = ['org.hibernate:hibernate:3.0.5 at jar', 'somegroup:someorg:1.0 at jar']
diff --git a/subprojects/gradle-docs/src/samples/userguide/files/sync/build.gradle b/subprojects/gradle-docs/src/samples/userguide/files/sync/build.gradle
new file mode 100644
index 0000000..5e4adc7
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguide/files/sync/build.gradle
@@ -0,0 +1,8 @@
+configurations { runtime }
+
+// START SNIPPET copy-dependencies
+task libs(type: Sync) {
+    from configurations.runtime
+    into "$buildDir/libs"
+}
+// END SNIPPET copy-dependencies
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/inputsAndOutputs/build.gradle b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/inputsAndOutputs/build.gradle
new file mode 100644
index 0000000..3af6cb0
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/inputsAndOutputs/build.gradle
@@ -0,0 +1,21 @@
+// START SNIPPET declare-inputs-and-outputs
+task transform {
+    srcFile = file('mountains.xml')
+    destDir = new File(buildDir, 'generated')
+    inputs.file srcFile
+    outputs.dir destDir
+// END SNIPPET declare-inputs-and-outputs
+    doLast {
+        println "Transforming source file."
+        destDir.mkdirs()
+        def mountains = new XmlParser().parse(srcFile)
+        mountains.mountain.each { mountain ->
+            def name = mountain.name[0].text()
+            def height = mountain.height[0].text()
+            def destFile = new File(destDir, "${name}.txt")
+            destFile.text = "$name -> ${height}\n"
+        }
+    }
+// START SNIPPET declare-inputs-and-outputs
+}
+// END SNIPPET declare-inputs-and-outputs
diff --git a/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/inputsAndOutputs/mountains.xml b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/inputsAndOutputs/mountains.xml
new file mode 100644
index 0000000..f353a4a
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/inputsAndOutputs/mountains.xml
@@ -0,0 +1,10 @@
+<mountains>
+    <mountain>
+        <name>Mount Everest</name>
+        <height>8848</height>
+    </mountain>
+    <mountain>
+        <name>K2</name>
+        <height>8611</height>
+    </mountain>
+</mountains>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/noInputsAndOutputs/build.gradle b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/noInputsAndOutputs/build.gradle
new file mode 100644
index 0000000..c7f980d
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/noInputsAndOutputs/build.gradle
@@ -0,0 +1,15 @@
+task transform {
+    srcFile = file('mountains.xml')
+    destDir = new File(buildDir, 'generated')
+    doLast {
+        println "Transforming source file."
+        destDir.mkdirs()
+        def mountains = new XmlParser().parse(srcFile)
+        mountains.mountain.each { mountain ->
+            def name = mountain.name[0].text()
+            def height = mountain.height[0].text()
+            def destFile = new File(destDir, "${name}.txt")
+            destFile.text = "$name -> ${height}\n"
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/noInputsAndOutputs/mountains.xml b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/noInputsAndOutputs/mountains.xml
new file mode 100644
index 0000000..f353a4a
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguide/tasks/incrementalBuild/noInputsAndOutputs/mountains.xml
@@ -0,0 +1,10 @@
+<mountains>
+    <mountain>
+        <name>Mount Everest</name>
+        <height>8848</height>
+    </mountain>
+    <mountain>
+        <name>K2</name>
+        <height>8611</height>
+    </mountain>
+</mountains>
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/userguide/tutorial/logging/build.gradle b/subprojects/gradle-docs/src/samples/userguide/tutorial/logging/build.gradle
new file mode 100644
index 0000000..fc82f85
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguide/tutorial/logging/build.gradle
@@ -0,0 +1,35 @@
+// START SNIPPET use-logger
+logger.quiet('An info log message which is always logged.')
+logger.error('An error log message.')
+logger.warn('A warning log message.')
+logger.lifecycle('A lifecycle info log message.')
+logger.info('An info log message.')
+logger.debug('A debug log message.')
+logger.trace('A trace log message.')
+// END SNIPPET use-logger
+
+// START SNIPPET use-println
+println 'A message which is logged at QUIET level'
+// END SNIPPET use-println
+
+// START SNIPPET capture-stdout
+logging.captureStandardOutput LogLevel.INFO
+println 'A message which is logged at INFO level'
+// END SNIPPET capture-stdout
+
+// START SNIPPET task-capture-stdout
+task logInfo {
+    logging.captureStandardOutput LogLevel.INFO
+    doFirst {
+        println 'A task message which is logged at INFO level'
+    }
+}
+// END SNIPPET task-capture-stdout
+
+// START SNIPPET use-slf4j
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+Logger slf4jLogger = LoggerFactory.getLogger('some-logger')
+slf4jLogger.info('An info log message logged using SLF4j')
+// END SNIPPET use-slf4j
diff --git a/subprojects/gradle-docs/src/samples/userguide/tutorial/projectReports/build.gradle b/subprojects/gradle-docs/src/samples/userguide/tutorial/projectReports/build.gradle
index 2d77a0c..c2d2433 100644
--- a/subprojects/gradle-docs/src/samples/userguide/tutorial/projectReports/build.gradle
+++ b/subprojects/gradle-docs/src/samples/userguide/tutorial/projectReports/build.gradle
@@ -17,22 +17,24 @@
 defaultTasks 'dists'
 
 allprojects {
+    version = '1.0-SNAPSHOT'
     task clean {
         description = "Deletes the build directory ($buildDir.name)"
         group = 'build'
     }
 }
 
-task libs {
-    dependsOn { subprojects*.libs }
-    description = 'Builds the JAR'
+task docs {
+    description = 'Builds the documentation'
 }
 
-// START SNIPPET add-task-to-report
 task dists {
-// END SNIPPET add-task-to-report
-    dependsOn libs
+    dependsOn { subprojects*.libs }
+    dependsOn docs
+}
+
 // START SNIPPET add-task-to-report
+dists {
     description = 'Builds the distribution'
     group = 'build'
 }
@@ -45,23 +47,36 @@ subprojects {
     repositories {
         mavenCentral()
     }
-    dependencies {
-        if (project.name == 'webapp') {
-        	compile "commons-io:commons-io:1.2"
-		} else {
-			compile "junit:junit:4.7"
-		}
+    task compile {
+        description = 'Compiles the source files'
     }
     task libs {
-        if (project.name == 'webapp') {
-            dependsOn ':api:libs' 
-        }
-        description = 'Builds the JAR'
         group = 'build'
+        description = 'Builds the JAR'
+        dependsOn compile
     }
     tasks.addRule(new TestRule())
 }
 
+project(':api') {
+// START SNIPPET project-description
+description = 'The shared API for the application'
+// END SNIPPET project-description
+    dependencies {
+        compile "org.codehaus.groovy:groovy-all:1.7.5"
+    }
+}
+
+project(':webapp') {
+    description = 'The Web application implementation'
+    dependencies {
+        compile project(path: ':api', configuration: 'compile'), "commons-io:commons-io:1.2"
+    }
+    libs {
+        dependsOn ':api:libs'
+    }
+}
+
 class TestRule implements Rule {
     public String getDescription() {
         'build<ConfigurationName>: builds the artifacts of the given configuration'
diff --git a/subprojects/gradle-docs/src/samples/userguide/wrapper/build.gradle b/subprojects/gradle-docs/src/samples/userguide/wrapper/build.gradle
index 4997f48..517f82e 100644
--- a/subprojects/gradle-docs/src/samples/userguide/wrapper/build.gradle
+++ b/subprojects/gradle-docs/src/samples/userguide/wrapper/build.gradle
@@ -1,6 +1,6 @@
 //START SNIPPET wrapper-simple
 task wrapper(type: Wrapper) {
-    gradleVersion = '0.6'
+    gradleVersion = '0.8'
 //END SNIPPET wrapper-simple
     jarPath = 'wrapper'
 //START SNIPPET wrapper-simple
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/dependencyListReport.out b/subprojects/gradle-docs/src/samples/userguideOutput/dependencyListReport.out
index 6a99a79..79cf988 100644
--- a/subprojects/gradle-docs/src/samples/userguideOutput/dependencyListReport.out
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/dependencyListReport.out
@@ -2,16 +2,21 @@
 ------------------------------------------------------------
 Root Project
 ------------------------------------------------------------
+
 No configurations
 
 ------------------------------------------------------------
-Project :api
+Project :api - The shared API for the application
 ------------------------------------------------------------
+
 compile
-|-----junit:junit:4.7:default
+\--- org.codehaus.groovy:groovy-all:1.7.5 [default]
 
 ------------------------------------------------------------
-Project :webapp
+Project :webapp - The Web application implementation
 ------------------------------------------------------------
+
 compile
-|-----commons-io:commons-io:1.2:default
++--- projectReports:api:1.0-SNAPSHOT [compile]
+|    \--- org.codehaus.groovy:groovy-all:1.7.5 [default]
+\--- commons-io:commons-io:1.2 [default]
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/generatorTask.out b/subprojects/gradle-docs/src/samples/userguideOutput/generatorTask.out
new file mode 100644
index 0000000..087425e
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/generatorTask.out
@@ -0,0 +1,2 @@
+:transform
+Transforming source file.
\ No newline at end of file
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/incrementalTask.out b/subprojects/gradle-docs/src/samples/userguideOutput/incrementalTask.out
new file mode 100644
index 0000000..1a36123
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/incrementalTask.out
@@ -0,0 +1 @@
+:transform UP-TO-DATE
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/projectListReport.out b/subprojects/gradle-docs/src/samples/userguideOutput/projectListReport.out
new file mode 100644
index 0000000..e0ede51
--- /dev/null
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/projectListReport.out
@@ -0,0 +1,7 @@
+
+Root project 'projectReports'
++--- Project ':api' - The shared API for the application
+\--- Project ':webapp' - The Web application implementation
+
+To see a list of the tasks of a project, run gradle <project-path>:tasks
+For example, try running gradle :api:tasks
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/propertyListReport.out b/subprojects/gradle-docs/src/samples/userguideOutput/propertyListReport.out
index fe4ea49..1fb0c2e 100644
--- a/subprojects/gradle-docs/src/samples/userguideOutput/propertyListReport.out
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/propertyListReport.out
@@ -1,15 +1,16 @@
 
 ------------------------------------------------------------
-Project :api
+Project :api - The shared API for the application
 ------------------------------------------------------------
+
 additionalProperties: {}
-all: [task ':api:clean', task ':api:libs']
+all: [task ':api:clean', task ':api:compile', task ':api:libs']
 allprojects: [project ':api']
 ant: org.gradle.api.internal.project.DefaultAntBuilder at 12345
 antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory at 12345
 artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler at 12345
 asDynamicObject: org.gradle.api.internal.DynamicObjectHelper at 12345
-asMap: {clean=task ':api:clean', libs=task ':api:libs'}
+asMap: {clean=task ':api:clean', compile=task ':api:compile', libs=task ':api:libs'}
 buildDir: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build
 buildDirName: build
 buildFile: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build.gradle
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/taskListAllReport.out b/subprojects/gradle-docs/src/samples/userguideOutput/taskListAllReport.out
index f769fd8..3e29c92 100644
--- a/subprojects/gradle-docs/src/samples/userguideOutput/taskListAllReport.out
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/taskListAllReport.out
@@ -2,10 +2,25 @@
 ------------------------------------------------------------
 Root Project
 ------------------------------------------------------------
+
 Default tasks: dists
 
 Build tasks
 -----------
-:clean - Deletes the build directory (build)
-:dists - Builds the distribution [:api:libs, :webapp:libs]
-    :libs - Builds the JAR
+clean - Deletes the build directory (build)
+api:clean - Deletes the build directory (build)
+webapp:clean - Deletes the build directory (build)
+dists - Builds the distribution [api:libs, webapp:libs]
+    docs - Builds the documentation
+api:libs - Builds the JAR
+    api:compile - Compiles the source files
+webapp:libs - Builds the JAR [api:libs]
+    webapp:compile - Compiles the source files
+
+Help tasks
+----------
+dependencies - Displays the dependencies of root project 'projectReports'.
+help - Displays a help message
+projects - Displays the sub-projects of root project 'projectReports'.
+properties - Displays the properties of root project 'projectReports'.
+tasks - Displays the tasks in root project 'projectReports'.
diff --git a/subprojects/gradle-docs/src/samples/userguideOutput/taskListReport.out b/subprojects/gradle-docs/src/samples/userguideOutput/taskListReport.out
index 59f4b71..b5a2aa5 100644
--- a/subprojects/gradle-docs/src/samples/userguideOutput/taskListReport.out
+++ b/subprojects/gradle-docs/src/samples/userguideOutput/taskListReport.out
@@ -2,9 +2,21 @@
 ------------------------------------------------------------
 Root Project
 ------------------------------------------------------------
+
 Default tasks: dists
 
 Build tasks
 -----------
-:clean - Deletes the build directory (build)
-:dists - Builds the distribution
+clean - Deletes the build directory (build)
+dists - Builds the distribution
+libs - Builds the JAR
+
+Help tasks
+----------
+dependencies - Displays the dependencies of root project 'projectReports'.
+help - Displays a help message
+projects - Displays the sub-projects of root project 'projectReports'.
+properties - Displays the properties of root project 'projectReports'.
+tasks - Displays the tasks in root project 'projectReports'.
+
+To see all tasks and more detail, run with --all.
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/AbstractXmlGeneratorTask.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/AbstractXmlGeneratorTask.groovy
index 342bf0f..f48cd9b 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/AbstractXmlGeneratorTask.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/AbstractXmlGeneratorTask.groovy
@@ -25,11 +25,6 @@ import org.gradle.listener.ListenerBroadcast
 class AbstractXmlGeneratorTask extends ConventionTask {
     ListenerBroadcast<Action> beforeConfiguredActions = new ListenerBroadcast<Action>(Action.class);
     ListenerBroadcast<Action> whenConfiguredActions = new ListenerBroadcast<Action>(Action.class);
-    ListenerBroadcast<Action> withXmlActions = new ListenerBroadcast<Action>(Action.class);
-
-    void withXml(Closure closure) {
-        withXmlActions.add("execute", closure);
-    }
 
     void beforeConfigured(Closure closure) {
         beforeConfiguredActions.add("execute", closure);
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy
index 5dd0224..fb76500 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseClasspath.groovy
@@ -22,9 +22,12 @@ import org.gradle.api.tasks.TaskAction
 import org.gradle.plugins.eclipse.model.internal.ModelFactory
 import org.gradle.plugins.eclipse.model.Container
 import org.gradle.plugins.eclipse.model.Classpath
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.Action
 
 /**
- * Generates an eclipse <i>.classpath</i> file.
+ * Generates an Eclipse <i>.classpath</i> file.
  *
  * @author Hans Dockter
  */
@@ -77,6 +80,8 @@ public class EclipseClasspath extends AbstractXmlGeneratorTask {
 
     protected ModelFactory modelFactory = new ModelFactory()
 
+    protected XmlTransformer withXmlActions = new XmlTransformer();
+
     def EclipseClasspath() {
         outputs.upToDateWhen { false }
     }
@@ -106,4 +111,24 @@ public class EclipseClasspath extends AbstractXmlGeneratorTask {
         assert variables != null
         this.variables.putAll variables
     }
+
+    /**
+     * Adds a closure to be called when the .classpath XML has been created. The XML is passed to the closure as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The closure can modify the XML.
+     *
+     * @param closure The closure to execute when the .classpath XML has been created.
+     */
+    void withXml(Closure closure) {
+        withXmlActions.addAction(closure);
+    }
+
+    /**
+     * Adds an action to be called when the .classpath XML has been created. The XML is passed to the action as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.
+     *
+     * @param action The action to execute when the .classpath XML has been created.
+     */
+    void withXml(Action<? super XmlProvider> action) {
+        withXmlActions.addAction(action);
+    }
 }
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy
index 8f4e294..fa7fdcd 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseProject.groovy
@@ -23,9 +23,12 @@ import org.gradle.plugins.eclipse.model.BuildCommand
 import org.gradle.plugins.eclipse.model.Link
 import org.gradle.plugins.eclipse.model.internal.ModelFactory
 import org.gradle.plugins.eclipse.model.Project
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.Action
 
 /**
- * Generates an eclipse <i>.project</i> file.
+ * Generates an Eclipse <i>.project</i> file.
  *
  * @author Hans Dockter
  */
@@ -67,7 +70,7 @@ public class EclipseProject extends AbstractXmlGeneratorTask {
      * The build commands to be added to this Eclipse project.
      */
     List<BuildCommand> buildCommands = []
-    
+
     /**
      * The links to be added to this Eclipse project.
      */
@@ -75,6 +78,8 @@ public class EclipseProject extends AbstractXmlGeneratorTask {
 
     protected ModelFactory modelFactory = new ModelFactory()
 
+    def XmlTransformer withXmlActions = new XmlTransformer();
+
     def EclipseProject() {
         outputs.upToDateWhen { false }
     }
@@ -109,7 +114,7 @@ public class EclipseProject extends AbstractXmlGeneratorTask {
      *
      * @param args A map with arguments, where the key is the name of the argument and the value the value.
      * @param buildCommand The name of the build command.
-     * @see #buildCommand(String) 
+     * @see #buildCommand(String)
      */
     void buildCommand(Map args, String buildCommand) {
         assert buildCommand != null
@@ -120,7 +125,7 @@ public class EclipseProject extends AbstractXmlGeneratorTask {
      * Adds a build command to the eclipse project.
      *
      * @param buildCommand The name of the build command
-     * @see #buildCommand(Map, String) 
+     * @see #buildCommand(Map, String)
      */
     void buildCommand(String buildCommand) {
         assert buildCommand != null
@@ -139,4 +144,24 @@ public class EclipseProject extends AbstractXmlGeneratorTask {
         }
         this.links.add(new Link(args.name, args.type, args.location, args.locationUri))
     }
+
+    /**
+     * Adds a closure to be called when the .project XML has been created. The XML is passed to the closure as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The closure can modify the XML.
+     *
+     * @param closure The closure to execute when the .project XML has been created.
+     */
+    void withXml(Closure closure) {
+        withXmlActions.addAction(closure);
+    }
+
+    /**
+     * Adds an action to be called when the .project XML has been created. The XML is passed to the action as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.
+     *
+     * @param action The action to execute when the .project XML has been created.
+     */
+    void withXml(Action<? super XmlProvider> action) {
+        withXmlActions.addAction(action);
+    }
 }
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy
index 5983d7a..c681855 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/EclipseWtp.groovy
@@ -24,6 +24,8 @@ import org.gradle.plugins.eclipse.model.internal.ModelFactory
 import org.gradle.plugins.eclipse.model.Wtp
 import org.gradle.plugins.eclipse.model.WbResource
 import org.gradle.plugins.eclipse.model.WbProperty
+import org.gradle.api.Action
+import org.gradle.listener.ListenerBroadcast
 
 /**
  * Generates Eclipse configuration files for Eclipse WTP.
@@ -100,6 +102,8 @@ public class EclipseWtp extends AbstractXmlGeneratorTask {
 
     protected ModelFactory modelFactory = new ModelFactory()
 
+    def ListenerBroadcast<Action> withXmlActions = new ListenerBroadcast<Action>(Action.class);
+
     def EclipseWtp() {
         outputs.upToDateWhen { false }
     }
@@ -107,7 +111,7 @@ public class EclipseWtp extends AbstractXmlGeneratorTask {
     @TaskAction
     protected void generateXml() {
         Wtp wtp = modelFactory.createWtp(this)
-        wtp.toXml(orgEclipseWstCommonComponentOutputFile, orgEclipseWstCommonProjectFacetCoreOutputFile)    
+        wtp.toXml(orgEclipseWstCommonComponentOutputFile, orgEclipseWstCommonProjectFacetCoreOutputFile)
     }
 
     /**
@@ -147,4 +151,8 @@ public class EclipseWtp extends AbstractXmlGeneratorTask {
     void resource(Map args) {
         resources.add(new WbResource(args.deployPath, args.sourcePath))
     }
+
+    void withXml(Closure closure) {
+        withXmlActions.add("execute", closure);
+    }
 }
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy
index caf6002..0aec4b8 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Classpath.groovy
@@ -15,8 +15,9 @@
  */
 package org.gradle.plugins.eclipse.model
 
-import org.gradle.listener.ListenerBroadcast
-import org.gradle.plugins.eclipse.EclipseClasspath
+import org.gradle.api.internal.XmlTransformer
+
+import org.gradle.api.Action
 
 /**
  * Represents the customizable elements of an eclipse classpath file. (via XML hooks everything is customizable).
@@ -31,18 +32,18 @@ class Classpath {
 
     private Node xml
 
-    private ListenerBroadcast withXmlActions
+    private XmlTransformer xmlTransformer
 
-    Classpath(EclipseClasspath eclipseClasspath, List entries, Reader inputXml) {
+    Classpath(Action<Classpath> beforeConfiguredAction, Action<Classpath> whenConfiguredAction, XmlTransformer xmlTransformer, List entries, Reader inputXml) {
         initFromXml(inputXml)
 
-        eclipseClasspath.beforeConfiguredActions.source.execute(this)
+        beforeConfiguredAction.execute(this)
 
         this.entries.addAll(entries)
         this.entries.unique()
-        this.withXmlActions = eclipseClasspath.withXmlActions
+        this.xmlTransformer = xmlTransformer
 
-        eclipseClasspath.whenConfiguredActions.source.execute(this)
+        whenConfiguredAction.execute(this)
     }
 
     private def initFromXml(Reader inputXml) {
@@ -76,7 +77,7 @@ class Classpath {
     }
 
     void toXml(File file) {
-        toXml(new FileWriter(file))
+        file.withWriter { Writer writer -> toXml(writer) }
     }
 
     def toXml(Writer writer) {
@@ -84,9 +85,7 @@ class Classpath {
         entries.each { ClasspathEntry entry ->
             entry.appendNode(xml)
         }
-        withXmlActions.source.execute(xml)
-
-        new XmlNodePrinter(new PrintWriter(writer)).print(xml)
+        xmlTransformer.transform(xml, writer)
     }
     
     boolean equals(o) {
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.java b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.java
index a22d65d..164aaf2 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.java
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/ClasspathEntry.java
@@ -18,6 +18,8 @@ package org.gradle.plugins.eclipse.model;
 import groovy.util.Node;
 
 /**
+ * Represents an entry in the Eclipse classpath.
+ * 
  * @author Hans Dockter
  */
 public interface ClasspathEntry {
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy
index 161bba6..cf7463a 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Project.groovy
@@ -15,7 +15,7 @@
  */
 package org.gradle.plugins.eclipse.model
 
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.internal.XmlTransformer
 import org.gradle.plugins.eclipse.EclipseProject
 
 /**
@@ -58,7 +58,7 @@ class Project {
 
     private Node xml
 
-    private ListenerBroadcast withXmlActions
+    private XmlTransformer xmlTransformer
 
     def Project(EclipseProject eclipseProjectTask, Reader inputXml) {
         initFromXml(inputXml)
@@ -77,7 +77,7 @@ class Project {
         this.buildCommands.addAll(eclipseProjectTask.buildCommands)
         this.buildCommands.unique()
         this.links.addAll(eclipseProjectTask.links);
-        this.withXmlActions = eclipseProjectTask.withXmlActions
+        this.xmlTransformer = eclipseProjectTask.withXmlActions
 
         eclipseProjectTask.whenConfiguredActions.source.execute(this)
     }
@@ -125,7 +125,7 @@ class Project {
     }
 
     void toXml(File file) {
-        toXml(new FileWriter(file))
+        file.withWriter {Writer writer -> toXml(writer)}
     }
 
     def toXml(Writer writer) {
@@ -141,9 +141,7 @@ class Project {
         addNaturesToXml()
         addBuildSpecToXml()
         addLinksToXml()
-        withXmlActions.source.execute(xml)
-
-        new XmlNodePrinter(new PrintWriter(writer)).print(xml)
+        xmlTransformer.transform(xml, writer)
     }
 
     private def addReferencedProjectsToXml() {
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy
index ee618fb..0ac424e 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/Wtp.groovy
@@ -97,7 +97,11 @@ class Wtp {
     }
 
     void toXml(File orgEclipseWstCommonComponentXmlFile, File orgEclipseWstCommonProjectFacetCoreXmlFile) {
-        toXml(new FileWriter(orgEclipseWstCommonComponentXmlFile), new FileWriter(orgEclipseWstCommonProjectFacetCoreXmlFile))
+        orgEclipseWstCommonComponentXmlFile.withWriter {Writer componentWriter ->
+            orgEclipseWstCommonProjectFacetCoreXmlFile.withWriter {Writer facetWriter ->
+                toXml(componentWriter, facetWriter)
+            }
+        }
     }
 
     def toXml(Writer orgEclipseWstCommonComponentXmlWriter, Writer orgEclipseWstCommonProjectFacetCoreXmlWriter) {
@@ -113,8 +117,13 @@ class Wtp {
                 'org.eclipse.wst.commons.component': orgEclipseWstCommonComponentXml,
                 'org.eclipse.wst.commons.project.facet.core': orgEclipseWstCommonProjectFacetCoreXml])
 
-        new XmlNodePrinter(new PrintWriter(orgEclipseWstCommonComponentXmlWriter)).print(orgEclipseWstCommonComponentXml)
-        new XmlNodePrinter(new PrintWriter(orgEclipseWstCommonProjectFacetCoreXmlWriter)).print(orgEclipseWstCommonProjectFacetCoreXml)
+        PrintWriter printWriter = new PrintWriter(orgEclipseWstCommonComponentXmlWriter)
+        new XmlNodePrinter(printWriter).print(orgEclipseWstCommonComponentXml)
+        printWriter.flush()
+
+        printWriter = new PrintWriter(orgEclipseWstCommonProjectFacetCoreXmlWriter)
+        new XmlNodePrinter(printWriter).print(orgEclipseWstCommonProjectFacetCoreXml)
+        printWriter.flush()
     }
 
     private def removeConfigurableDataFromXml() {
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy
index 116a24d..c3b16bd 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/internal/ClasspathFactory.groovy
@@ -33,7 +33,7 @@ class ClasspathFactory {
         List entries = getEntriesFromSourceSets(eclipseClasspath.sourceSets, eclipseClasspath.project)
         entries.addAll(getEntriesFromContainers(eclipseClasspath.getContainers()))
         entries.addAll(getEntriesFromConfigurations(eclipseClasspath))
-        return new Classpath(eclipseClasspath, entries, inputReader)
+        return new Classpath(eclipseClasspath.beforeConfiguredActions.source, eclipseClasspath.whenConfiguredActions.source, eclipseClasspath.withXmlActions, entries, inputReader)
     }
 
     List getEntriesFromSourceSets(def sourceSets, def project) {
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/package-info.java b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/package-info.java
new file mode 100644
index 0000000..a619e08
--- /dev/null
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/model/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for the model used by the {@link org.gradle.plugins.eclipse.EclipsePlugin}.
+ */
+package org.gradle.plugins.eclipse.model;
diff --git a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java
index d6de80b..d5f39db 100644
--- a/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java
+++ b/subprojects/gradle-eclipse/src/main/groovy/org/gradle/plugins/eclipse/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * The eclipse project generation {@link org.gradle.api.Task} implementations.
+ * A {link org.gradle.api.Plugin} for generating Eclipse files. 
  */
 package org.gradle.plugins.eclipse;
diff --git a/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy b/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy
index a7353ea..4b32441 100644
--- a/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy
+++ b/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ClasspathTest.groovy
@@ -17,11 +17,11 @@ package org.gradle.plugins.eclipse.model;
 
 
 import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.internal.XmlTransformer
 import org.gradle.util.TemporaryFolder
 import org.junit.Rule
 import spock.lang.Specification
-import org.gradle.plugins.eclipse.EclipseClasspath
 
 /**
  * @author Hans Dockter
@@ -103,14 +103,13 @@ public class ClasspathTest extends Specification {
 
     def beforeConfigured() {
         def constructorEntries = [createSomeLibrary()]
-        ListenerBroadcast beforeConfiguredActions = new ListenerBroadcast(Action)
-        beforeConfiguredActions.add("execute") { Classpath classpath ->
+        Action beforeConfiguredAction = { Classpath classpath ->
             classpath.entries.clear()
-        }
+        } as Action
 
         when:
         Classpath classpath = createClasspath(entries: constructorEntries, reader: customClasspathReader,
-                beforeConfiguredActions: beforeConfiguredActions)
+                beforeConfiguredActions: beforeConfiguredAction)
 
         then:
         createClasspath(reader: getToXmlReader(classpath)).entries == DEFAULT_ENTRIES + constructorEntries
@@ -121,12 +120,11 @@ public class ClasspathTest extends Specification {
         def configureActionEntry = createSomeLibrary()
         configureActionEntry.path = constructorEntry.path + 'Other'
 
-        ListenerBroadcast whenConfiguredActions = new ListenerBroadcast(Action)
-        whenConfiguredActions.add("execute") { Classpath classpath ->
+        Action whenConfiguredActions = { Classpath classpath ->
             assert classpath.entries.contains((CUSTOM_ENTRIES as List)[0])
             assert classpath.entries.contains(constructorEntry)
             classpath.entries.add(configureActionEntry)
-        }
+        } as Action
 
         when:
         Classpath classpath = createClasspath(entries: [constructorEntry], reader: customClasspathReader,
@@ -138,12 +136,12 @@ public class ClasspathTest extends Specification {
     }
 
     def withXml() {
-        ListenerBroadcast withXmlActions = new ListenerBroadcast(Action)
+        XmlTransformer withXmlActions = new XmlTransformer()
         Classpath classpath = createClasspath(reader: customClasspathReader, withXmlActions: withXmlActions)
 
         when:
-        withXmlActions.add("execute") { Node xml ->
-            xml.classpathentry.find { it. at kind == 'output' }. at path = 'newPath'
+        withXmlActions.addAction { XmlProvider xml ->
+            xml.asNode().classpathentry.find { it. at kind == 'output' }. at path = 'newPath'
         }
 
         then:
@@ -159,13 +157,10 @@ public class ClasspathTest extends Specification {
     }
 
     private Classpath createClasspath(Map customArgs) {
-        ListenerBroadcast dummyBroadcast = new ListenerBroadcast(Action)
-        Map args = [entries: [], reader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: dummyBroadcast] + customArgs
-        EclipseClasspath eclipseClasspathStub = Mock()
-        eclipseClasspathStub.getBeforeConfiguredActions() >> args.beforeConfiguredActions
-        eclipseClasspathStub.getWhenConfiguredActions() >> args.whenConfiguredActions
-        eclipseClasspathStub.getWithXmlActions() >> args.withXmlActions
-        return new Classpath(eclipseClasspathStub, args.entries, args.reader)
+        Action dummyBroadcast = Mock()
+        XmlTransformer transformer = new XmlTransformer()
+        Map args = [entries: [], reader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: transformer] + customArgs
+        return new Classpath(args.beforeConfiguredActions, args.whenConfiguredActions, args.withXmlActions, args.entries, args.reader)
     }
 
     private StringReader getToXmlReader(Classpath classpath) {
diff --git a/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy b/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy
index ae19c77..1810266 100644
--- a/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy
+++ b/subprojects/gradle-eclipse/src/test/groovy/org/gradle/plugins/eclipse/model/ProjectTest.groovy
@@ -22,6 +22,8 @@ import org.gradle.util.TemporaryFolder
 import org.junit.Rule
 import spock.lang.Specification
 import org.gradle.plugins.eclipse.EclipseProject
+import org.gradle.api.internal.XmlTransformer
+import org.gradle.api.artifacts.maven.XmlProvider
 
 /**
  * @author Hans Dockter
@@ -154,12 +156,13 @@ public class ProjectTest extends Specification {
     }
 
     def withXml() {
-        ListenerBroadcast withXmlActions = new ListenerBroadcast(Action)
+        XmlTransformer withXmlActions = new XmlTransformer()
         Project project = createProject(reader: customProjectReader, withXmlActions: withXmlActions)
 
         when:
         def newName
-        withXmlActions.add("execute") { Node xml ->
+        withXmlActions.addAction { XmlProvider provider ->
+            def xml = provider.asNode()
             newName = xml.name.text() + 'x'
             xml.remove(xml.name)
             xml.appendNode('name', newName)
@@ -175,8 +178,9 @@ public class ProjectTest extends Specification {
 
     private Project createProject(Map customArgs) {
         ListenerBroadcast dummyBroadcast = new ListenerBroadcast(Action)
+        XmlTransformer transformer = new XmlTransformer()
         Map args = [name: null, comment: null, referencedProjects: [] as Set, natures: [], buildCommands: [],
-                links: [] as Set, reader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: dummyBroadcast] + customArgs
+                links: [] as Set, reader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: transformer] + customArgs
         EclipseProject eclipseProjectStub = Mock()
         eclipseProjectStub.getProjectName() >> args.name
         eclipseProjectStub.getComment() >> args.comment
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.groovy
index 6fc247c..885a350 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaModule.groovy
@@ -16,15 +16,22 @@
 package org.gradle.plugins.idea
 
 import org.gradle.api.Action
+import org.gradle.api.artifacts.maven.XmlProvider
 import org.gradle.api.internal.ConventionTask
+import org.gradle.api.internal.XmlTransformer
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
 import org.gradle.api.specs.Specs
 import org.gradle.listener.ListenerBroadcast
+import org.gradle.plugins.idea.model.ModuleLibrary
+import org.gradle.plugins.idea.model.Path
+import org.gradle.plugins.idea.model.PathFactory
+import org.gradle.plugins.idea.model.VariableReplacement
 import org.gradle.api.artifacts.*
 import org.gradle.api.tasks.*
-import org.gradle.plugins.idea.model.*
 
 /**
+ * Generates an IDEA module file.
+ *
  * @author Hans Dockter
  */
 public class IdeaModule extends ConventionTask {
@@ -103,7 +110,7 @@ public class IdeaModule extends ConventionTask {
      */
     @InputFiles @Optional
     File gradleCacheHome
-                                 
+
     /**
      * The keys of this map are the Intellij scopes. Each key points to another map that has two keys, plus and minus.
      * The values of those keys are sets of  {@link org.gradle.api.artifacts.Configuration}  objects. The files of the
@@ -113,7 +120,7 @@ public class IdeaModule extends ConventionTask {
 
     private ListenerBroadcast<Action> beforeConfiguredActions = new ListenerBroadcast<Action>(Action.class);
     private ListenerBroadcast<Action> whenConfiguredActions = new ListenerBroadcast<Action>(Action.class);
-    private ListenerBroadcast<Action> withXmlActions = new ListenerBroadcast<Action>(Action.class);
+    private XmlTransformer withXmlActions = new XmlTransformer();
 
     def IdeaModule() {
         outputs.upToDateWhen { false }
@@ -123,12 +130,12 @@ public class IdeaModule extends ConventionTask {
     void updateXML() {
         Reader xmlreader = getOutputFile().exists() ? new FileReader(getOutputFile()) : null;
         org.gradle.plugins.idea.model.Module module = new org.gradle.plugins.idea.model.Module(getContentPath(), getSourcePaths(), getTestSourcePaths(), getExcludePaths(), getOutputPath(), getTestOutputPath(),
-                getDependencies(), getVariableReplacement(), javaVersion, xmlreader, beforeConfiguredActions, whenConfiguredActions, withXmlActions)
-        module.toXml(new FileWriter(getOutputFile()))
+                getDependencies(), getVariableReplacement(), javaVersion, xmlreader, beforeConfiguredActions.source, whenConfiguredActions.source, withXmlActions)
+        getOutputFile().withWriter {Writer writer -> module.toXml(writer)}
     }
 
     protected Path getContentPath() {
-       getPath(project.projectDir)
+        getPath(project.projectDir)
     }
 
     protected Path getOutputPath() {
@@ -286,11 +293,31 @@ public class IdeaModule extends ConventionTask {
     }
 
     protected Path getPath(File file) {
-        new Path(getOutputFile().parentFile, '$MODULE_DIR$', file)
+        PathFactory factory = new PathFactory()
+        factory.addPathVariable('MODULE_DIR', getOutputFile().parentFile)
+        return factory.path(file)
     }
 
+    /**
+     * Adds a closure to be called when the IML XML has been created. The XML is passed to the closure as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The closure can modify the XML.
+     *
+     * @param closure The closure to execute when the IML XML has been created.
+     * @return this
+     */
     void withXml(Closure closure) {
-        withXmlActions.add("execute", closure);
+        withXmlActions.addAction(closure)
+    }
+
+    /**
+     * Adds an action to be called when the IML XML has been created. The XML is passed to the action as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.
+     *
+     * @param closure The action to execute when the IML XML has been created.
+     * @return this
+     */
+    void withXml(Action<XmlProvider> action) {
+        withXmlActions.addAction(action)
     }
 
     void beforeConfigured(Closure closure) {
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy
index 5d6a0f1..1203921 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaProject.groovy
@@ -15,20 +15,19 @@
  */
 package org.gradle.plugins.idea
 
-import org.gradle.api.DefaultTask
-
-import org.gradle.api.tasks.TaskAction
-
 import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.DefaultTask
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.internal.XmlTransformer
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.OutputFile
-import org.gradle.plugins.idea.model.Path
-import org.gradle.plugins.idea.model.Project
+import org.gradle.api.tasks.TaskAction
+import org.gradle.listener.ListenerBroadcast
 import org.gradle.plugins.idea.model.ModulePath
+import org.gradle.plugins.idea.model.Project
 
 /**
- * A task that generates and Idea ipr file.
+ * Generates an IDEA project file.
  *
  * @author Hans Dockter
  */
@@ -59,7 +58,7 @@ public class IdeaProject extends DefaultTask {
 
     private ListenerBroadcast<Action> beforeConfiguredActions = new ListenerBroadcast<Action>(Action.class);
     private ListenerBroadcast<Action> whenConfiguredActions = new ListenerBroadcast<Action>(Action.class);
-    private ListenerBroadcast<Action> withXmlActions = new ListenerBroadcast<Action>(Action.class);
+    private XmlTransformer withXmlActions = new XmlTransformer();
 
     def IdeaProject() {
         outputs.upToDateWhen { false }
@@ -68,35 +67,39 @@ public class IdeaProject extends DefaultTask {
     @TaskAction
     void updateXML() {
         Reader xmlreader = outputFile.exists() ? new FileReader(outputFile) : null;
-        Set modules = subprojects.collect { subproject ->
+        Set modules = subprojects.inject(new LinkedHashSet()) { result, subproject ->
             if (subproject.plugins.hasPlugin(IdeaPlugin)) {
                 File imlFile = subproject.ideaModule.outputFile
-                new ModulePath(outputFile.parentFile, '$PROJECT_DIR$', imlFile)
+                result << new ModulePath(outputFile.parentFile, '$PROJECT_DIR$', imlFile)
             }
+            result
         }
-        Project ideaProject = new Project(modules, javaVersion, wildcards, xmlreader, beforeConfiguredActions, whenConfiguredActions, withXmlActions)
-        ideaProject.toXml(new FileWriter(outputFile))
+        Project ideaProject = new Project(modules, javaVersion, wildcards, xmlreader,
+                beforeConfiguredActions.source, whenConfiguredActions.source, withXmlActions)
+        outputFile.withWriter {Writer writer -> ideaProject.toXml(writer)}
     }
 
     /**
-     * Returns a relative URL to the given file in the standard URL format used by IDEA.  The resulting
-     * URL is relative to the project output directory, using the $PROJECT_DIR$ macro.
-     * @param file The file to which the relative path should point.
-     * @return The relative URL as a String. 
+     * Adds a closure to be called when the IPR XML has been created. The XML is passed to the closure as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The closure can modify the XML.
+     *
+     * @param closure The closure to execute when the IPR XML has been created.
+     * @return this
      */
-    String projectURL(File file) {
-       return new Path(outputFile.parentFile, '$PROJECT_DIR$', file).url
+    IdeaProject withXml(Closure closure) {
+        withXmlActions.addAction(closure)
+        return this;
     }
 
     /**
-     * Adds a closure to be called when the ipr xml has been created. The xml is passed to the closure as a
-     * parameter in form of a {@link groovy.util.Node}. The xml might be modified.
+     * Adds an action to be called when the IPR XML has been created. The XML is passed to the action as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.
      *
-     * @param closure The closure to execute when the ipr xml has been created.
+     * @param closure The action to execute when the IPR XML has been created.
      * @return this
      */
-    IdeaProject withXml(Closure closure) {
-        withXmlActions.add("execute", closure);
+    IdeaProject withXml(Action<? super XmlProvider> action) {
+        withXmlActions.addAction(action)
         return this;
     }
 
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy
index b9769aa..9a90e9c 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/IdeaWorkspace.groovy
@@ -15,14 +15,17 @@
  */
 package org.gradle.plugins.idea
 
-import org.gradle.api.Action
 import org.gradle.api.DefaultTask
+import org.gradle.api.internal.XmlTransformer
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.TaskAction
-import org.gradle.listener.ListenerBroadcast
 import org.gradle.plugins.idea.model.Workspace
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.Action
 
 /**
+ * Generates an IDEA workspace file.
+ *
  * @author Hans Dockter
  */
 public class IdeaWorkspace extends DefaultTask {
@@ -32,7 +35,7 @@ public class IdeaWorkspace extends DefaultTask {
     @OutputFile
     File outputFile
 
-    private ListenerBroadcast<Action> withXmlActions = new ListenerBroadcast<Action>(Action.class);
+    private XmlTransformer withXmlActions = new XmlTransformer()
 
     def IdeaWorkspace() {
         outputs.upToDateWhen { false }
@@ -42,10 +45,28 @@ public class IdeaWorkspace extends DefaultTask {
     void updateXML() {
         Reader xmlreader = outputFile.exists() ? new FileReader(outputFile) : null;
         Workspace workspace = new Workspace(xmlreader, withXmlActions)
-        workspace.toXml(new FileWriter(outputFile))
+        outputFile.withWriter { Writer writer -> workspace.toXml(writer) }
     }
 
+    /**
+     * Adds a closure to be called when the IWS XML has been created. The XML is passed to the closure as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The closure can modify the XML.
+     *
+     * @param closure The closure to execute when the IWS XML has been created.
+     * @return this
+     */
     void withXml(Closure closure) {
-        withXmlActions.add("execute", closure);
+        withXmlActions.addAction(closure);
+    }
+
+    /**
+     * Adds an action to be called when the IWS XML has been created. The XML is passed to the action as a
+     * parameter in form of a {@link org.gradle.api.artifacts.maven.XmlProvider}. The action can modify the XML.
+     *
+     * @param closure The action to execute when the IWS XML has been created.
+     * @return this
+     */
+    void withXml(Action<? super XmlProvider> action) {
+        withXmlActions.addAction(action)
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java
index 2b9ade1..89ef6ef 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Dependency.java
@@ -18,6 +18,8 @@ package org.gradle.plugins.idea.model;
 import groovy.util.Node;
 
 /**
+ * Represents a dependency of an IDEA module.
+ *
  * @author Hans Dockter
  */
 public interface Dependency {
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy
index 94c8fed..73b0ebc 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Module.groovy
@@ -16,7 +16,7 @@
 package org.gradle.plugins.idea.model
 
 import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.internal.XmlTransformer
 
 /**
  * Represents the customizable elements of an iml (via XML hooks everything of the iml is customizable).
@@ -66,15 +66,15 @@ class Module {
 
     private Node xml
 
-    private ListenerBroadcast<Action> withXmlActions
+    private XmlTransformer withXmlActions
 
     def Module(Path contentPath, Set sourceFolders, Set testSourceFolders, Set excludeFolders, Path outputDir, Path testOutputDir, Set dependencies,
                VariableReplacement dependencyVariableReplacement, String javaVersion, Reader inputXml,
-               ListenerBroadcast<Action> beforeConfiguredActions, ListenerBroadcast<Action> whenConfiguredActions,
-               ListenerBroadcast<Action> withXmlActions) {
+               Action<Module> beforeConfiguredAction, Action<Module> whenConfiguredAction,
+               XmlTransformer withXmlActions) {
         initFromXml(inputXml, dependencyVariableReplacement)
 
-        beforeConfiguredActions.source.execute(this)
+        beforeConfiguredAction.execute(this)
 
         this.contentPath = contentPath
         this.sourceFolders.addAll(sourceFolders);
@@ -92,7 +92,7 @@ class Module {
         }
         this.withXmlActions = withXmlActions;
 
-        whenConfiguredActions.source.execute(this)
+        whenConfiguredAction.execute(this)
     }
 
     private def initFromXml(Reader inputXml, VariableReplacement dependencyVariableReplacement) {
@@ -171,9 +171,7 @@ class Module {
         removeDependenciesFromXml()
         addDependenciesToXml()
 
-        withXmlActions.source.execute(xml)
-
-        new XmlNodePrinter(new PrintWriter(writer)).print(xml)
+        withXmlActions.transform(xml, writer)
     }
 
     private def addJdkToXml() {
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy
index fdf75aa..bfee4c0 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/ModulePath.groovy
@@ -25,28 +25,23 @@ class ModulePath extends Path {
     /**
      * The path string of this path.
      */
-    String path
+    final String path
 
-    def ModulePath(rootDir, rootDirString, file) {
+    def ModulePath(File rootDir, String rootDirString, File file) {
         super(rootDir, rootDirString, file)
-        path = getRelativePath(rootDir, rootDirString, file)
+        path = relPath
     }
 
-    def ModulePath(url) {
-        super(url)
-    }
-
-    def ModulePath(url, path) {
+    def ModulePath(String url, String path) {
         super(url)
         assert path != null
         this.path = path;
     }
 
-
     boolean equals(o) {
         if (this.is(o)) { return true }
 
-        if (getClass() != o.class) { return false }
+        if (o== null || getClass() != o.class) { return false }
         if (!super.equals(o)) { return false }
 
         ModulePath that = (ModulePath) o;
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy
index ef7979a..9e46c39 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Path.groovy
@@ -24,23 +24,35 @@ class Path {
     /**
      * The url of the path. Must not be null
      */
-    String url
+    final String url
 
-    def Path(rootDir, rootDirString, file) {
-        String path = getRelativePath(rootDir, rootDirString, file)
-        url = relativePathToURI(path)
+    /**
+     * The relative path of the path. Must not be null
+     */
+    final String relPath
+
+    def Path(File rootDir, String rootDirString, File file) {
+        relPath = getRelativePath(rootDir, rootDirString, file)
+        url = relativePathToURI(relPath)
+    }
+
+    def Path(File file) {
+        // IDEA doesn't like the result of file.toURI() so use the absolute path instead
+        relPath = file.absolutePath.replace(File.separator, '/')
+        url = relativePathToURI(relPath)
     }
 
-    def Path(url) {
+    def Path(String url) {
+        this.relPath = null
         this.url = url
     }
 
     public static String getRelativePath(File rootDir, String rootDirString, File file) {
         String relpath = getRelativePath(rootDir, file)
-        return rootDirString + '/' + relpath
+        return relpath != null ? rootDirString + '/' + relpath : file.absolutePath.replace(File.separator, '/')
     }
 
-    public static String relativePathToURI(String relpath) {
+    private String relativePathToURI(String relpath) {
         if (relpath.endsWith('.jar')) {
             return 'jar://' + relpath + '!/';
         } else {
@@ -49,7 +61,7 @@ class Path {
     }
 
     // This gets a relative path even if neither path is an ancestor of the other.
-    // implemenation taken from http://www.devx.com/tips/Tip/13737 and slighly modified
+    // implementation taken from http://www.devx.com/tips/Tip/13737 and slighly modified
     //@param relativeTo  the destinationFile
     //@param fromFile    where the relative path starts
 
@@ -59,10 +71,11 @@ class Path {
 
     private static List getPathList(File f) {
         List list = []
-        File r = f.getCanonicalFile()
+        File r = f.canonicalFile
         while (r != null) {
-            list.add(r.getName())
-            r = r.getParentFile()
+            File parent = r.parentFile
+            list.add(parent ? r.name : r.absolutePath)
+            r = parent
         }
 
         return list
@@ -74,6 +87,12 @@ class Path {
         // eliminate the common root
         int i = r.size() - 1
         int j = f.size() - 1
+
+        if (r[i] != f[j]) {
+            // no common root
+            return null
+        }
+
         while ((i >= 0) && (j >= 0) && (r[i] == f[j])) {
             i--
             j--
@@ -81,22 +100,25 @@ class Path {
 
         // for each remaining level in the relativeTo path, add a ..
         for (; i >= 0; i--) {
-            s.append('..').append('/')
+            s.append('../')
         }
 
         // for each level in the file path, add the path
         for (; j >= 1; j--) {
             s.append(f[j]).append('/')
         }
+        // add the file name
+        if (j == 0) {
+            s.append(f[j])
+        }
 
-        // add the file name and return the result
-        return s.append(f[j]).toString()
+        return s.toString()
     }
 
     boolean equals(o) {
         if (this.is(o)) { return true }
 
-        if (getClass() != o.class) { return false }
+        if (o == null || getClass() != o.class) { return false }
 
         Path path = (Path) o;
 
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/PathFactory.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/PathFactory.groovy
new file mode 100644
index 0000000..4e3d48a
--- /dev/null
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/PathFactory.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.idea.model
+
+class PathFactory {
+    private final List<Map> variables = []
+
+    void addPathVariable(String name, File dir) {
+        variables << [name: "\$${name}\$", prefix: dir.absolutePath + File.separator, dir: dir]
+    }
+
+    Path path(File file) {
+        Map match = null
+        for (variable in variables) {
+            if (file.absolutePath == variable.dir.absolutePath) {
+                match = variable
+                break
+            }
+            if (file.absolutePath.startsWith(variable.prefix)) {
+                if (!match || variable.prefix.startsWith(match.prefix)) {
+                    match = variable
+                }
+            }
+        }
+        
+        if (match) {
+            return new Path(match.dir, match.name, file)
+        }
+
+        return new Path(file)
+    }
+
+    Path path(String url) {
+        return new Path(url)
+    }
+}
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy
index c002ede..dd743bf 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Project.groovy
@@ -15,8 +15,8 @@
  */
 package org.gradle.plugins.idea.model
 
-import org.gradle.listener.ListenerBroadcast
 import org.gradle.api.Action
+import org.gradle.api.internal.XmlTransformer
 
 /**
  * Represents the customizable elements of an ipr (via XML hooks everything of the ipr is customizable).
@@ -25,7 +25,7 @@ import org.gradle.api.Action
  */
 class Project {
     /**
-     * A set of {@link ModulePath} instances pointing to the modules contained in the ipr.
+     * A set of {@link Path} instances pointing to the modules contained in the ipr.
      */
     Set modulePaths = []
 
@@ -41,19 +41,19 @@ class Project {
 
     private Node xml
     
-    private ListenerBroadcast<Action> withXmlActions
+    private XmlTransformer withXmlActions
 
-    def Project(Set modulePaths, String javaVersion, Set wildcards, Reader inputXml, ListenerBroadcast<Action> beforeConfiguredActions,
-                ListenerBroadcast<Action> whenConfiguredActions, ListenerBroadcast<Action> withXmlActions) {
+    def Project(Set modulePaths, String javaVersion, Set wildcards, Reader inputXml, Action<Project> beforeConfiguredAction,
+                Action<Project> whenConfiguredAction, XmlTransformer withXmlActions) {
         initFromXml(inputXml, javaVersion)
 
-        beforeConfiguredActions.source.execute(this)
+        beforeConfiguredAction.execute(this)
 
         this.modulePaths.addAll(modulePaths)
         this.wildcards.addAll(wildcards)
         this.withXmlActions = withXmlActions
 
-        whenConfiguredActions.source.execute(this)
+        whenConfiguredAction.execute(this)
     }
 
     private def initFromXml(Reader inputXml, String javaVersion) {
@@ -97,9 +97,7 @@ class Project {
         findProjectRootManager(). at languageLevel = jdk.languageLevel
         findProjectRootManager().@'project-jdk-name' = jdk.projectJdkName
 
-        withXmlActions.source.execute(xml)
-
-        new XmlNodePrinter(new PrintWriter(writer)).print(xml)
+        withXmlActions.transform(xml, writer)
     }
 
     private def findProjectRootManager() {
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.groovy b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.groovy
index ca61fda..d005622 100644
--- a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.groovy
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/Workspace.groovy
@@ -15,8 +15,7 @@
  */
 package org.gradle.plugins.idea.model
 
-import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.internal.XmlTransformer
 
 /**
  * Represents the customizable elements of an ipr (via XML hooks everything of the ipr is customizable).
@@ -27,9 +26,9 @@ import org.gradle.listener.ListenerBroadcast
 class Workspace {
     private Node xml
 
-    private ListenerBroadcast<Action> withXmlActions
+    private XmlTransformer withXmlActions
 
-    def Workspace(Reader inputXml, ListenerBroadcast<Action> withXmlActions) {
+    def Workspace(Reader inputXml, XmlTransformer withXmlActions) {
         initFromXml(inputXml)
 
         this.withXmlActions = withXmlActions
@@ -41,8 +40,6 @@ class Workspace {
     }
 
     def toXml(Writer writer) {
-        withXmlActions.source.execute(xml)
-
-        new XmlNodePrinter(new PrintWriter(writer)).print(xml)
+        withXmlActions.transform(xml, writer)
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/package-info.java b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/package-info.java
new file mode 100644
index 0000000..5d53666
--- /dev/null
+++ b/subprojects/gradle-idea/src/main/groovy/org/gradle/plugins/idea/model/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for the model used by the {@link org.gradle.plugins.idea.IdeaPlugin}. 
+ */
+package org.gradle.plugins.idea.model;
diff --git a/subprojects/gradle-idea/src/main/resources/org/gradle/plugins/idea/package-info.java b/subprojects/gradle-idea/src/main/resources/org/gradle/plugins/idea/package-info.java
new file mode 100644
index 0000000..654d077
--- /dev/null
+++ b/subprojects/gradle-idea/src/main/resources/org/gradle/plugins/idea/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A {link org.gradle.api.Plugin} for generating IDEA files. 
+ */
+package org.gradle.plugins.idea;
diff --git a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModulePathTest.groovy b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModulePathTest.groovy
new file mode 100644
index 0000000..e4b2169
--- /dev/null
+++ b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModulePathTest.groovy
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.idea.model
+
+import spock.lang.Specification
+import org.gradle.util.Matchers
+
+class ModulePathTest extends Specification {
+    def pathsAreEqualWhenTheirUrlAndPathAreEqual() {
+        expect:
+        Matchers.strictlyEquals(new ModulePath('url', 'path'), new ModulePath('url', 'path'))
+        new ModulePath('url', 'path') != new ModulePath('url', 'other')
+        new ModulePath('url', 'path') != new ModulePath('other', 'path')
+    }
+}
diff --git a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy
index 8171347..f7a4823 100644
--- a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy
+++ b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ModuleTest.groovy
@@ -16,7 +16,8 @@
 package org.gradle.plugins.idea.model
 
 import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.internal.XmlTransformer
 import spock.lang.Specification
 
 /**
@@ -145,10 +146,9 @@ class ModuleTest extends Specification {
 
     def beforeConfigured() {
         def constructorSourceFolders = [new Path('a')] as Set
-        ListenerBroadcast beforeConfiguredActions = new ListenerBroadcast(Action)
-        beforeConfiguredActions.add("execute") { Module module ->
+        Action beforeConfiguredActions = { Module module ->
             module.sourceFolders.clear()
-        }
+        } as Action
 
         when:
         module = createModule(sourceFolders: constructorSourceFolders, reader: customModuleReader, beforeConfiguredActions: beforeConfiguredActions)
@@ -161,12 +161,11 @@ class ModuleTest extends Specification {
         def constructorSourceFolder = new Path('a')
         def configureActionSourceFolder = new Path("c")
 
-        ListenerBroadcast whenConfiguredActions = new ListenerBroadcast(Action)
-        whenConfiguredActions.add("execute") { Module module ->
+        Action whenConfiguredActions = { Module module ->
             assert module.sourceFolders.contains((CUSTOM_SOURCE_FOLDERS as List)[0])
             assert module.sourceFolders.contains(constructorSourceFolder)
             module.sourceFolders.add(configureActionSourceFolder)
-        }
+        } as Action
 
         when:
         module = createModule(sourceFolders: [constructorSourceFolder] as Set, reader: customModuleReader,
@@ -183,12 +182,13 @@ class ModuleTest extends Specification {
     }
 
     def withXml() {
-        ListenerBroadcast withXmlActions = new ListenerBroadcast(Action)
+        XmlTransformer withXmlActions = new XmlTransformer()
         module = createModule(reader: customModuleReader, withXmlActions: withXmlActions)
 
         when:
         def modifiedVersion
-        withXmlActions.add("execute") { xml ->
+        withXmlActions.addAction { XmlProvider provider ->
+            def xml = provider.asNode()
             xml. at version += 'x'
             modifiedVersion = xml. at version
         }
@@ -206,10 +206,11 @@ class ModuleTest extends Specification {
     }
 
     private Module createModule(Map customArgs) {
-        ListenerBroadcast dummyBroadcast = new ListenerBroadcast(Action)
+        Action dummyBroadcast = Mock()
+        XmlTransformer xmlTransformer = new XmlTransformer()
         Map args = [contentPath: null, sourceFolders: [] as Set, testSourceFolders: [] as Set, excludeFolders: [] as Set, outputDir: null, testOutputDir: null,
-                moduleLibraries: [] as Set, dependencyVariableReplacement: VariableReplacement.NO_REPLACEMENT, javaVersion: null, 
-                reader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: dummyBroadcast] + customArgs
+                moduleLibraries: [] as Set, dependencyVariableReplacement: VariableReplacement.NO_REPLACEMENT, javaVersion: null,
+                reader: null, beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: xmlTransformer] + customArgs
         return new Module(args.contentPath, args.sourceFolders, args.testSourceFolders, args.excludeFolders, args.outputDir, args.testOutputDir,
                 args.moduleLibraries, args.dependencyVariableReplacement, args.javaVersion, args.reader,
                 args.beforeConfiguredActions, args.whenConfiguredActions, args.withXmlActions)
diff --git a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/PathFactoryTest.groovy b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/PathFactoryTest.groovy
new file mode 100644
index 0000000..e0588bf
--- /dev/null
+++ b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/PathFactoryTest.groovy
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.idea.model
+
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+class PathFactoryTest extends Specification {
+    @Rule TemporaryFolder tmpDir = new TemporaryFolder()
+    final PathFactory factory = new PathFactory()
+
+    def createsPathForAFileUnderARootDir() {
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+        def path = factory.path(tmpDir.file('a', 'b'))
+        path.url == 'file://$ROOT_DIR$/a/b'
+    }
+
+    def createsPathForAFileNotUnderARootDir() {
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+        def file = tmpDir.dir.parentFile.file('a')
+        def relpath = file.absolutePath.replace(File.separator, '/')
+
+        expect:
+        def path = factory.path(file)
+        path.url == "file://$relpath"
+    }
+
+    def usesTheClosestAncestorRootDirForAFileUnderMultipleRootDirs() {
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+        factory.addPathVariable('SUB_DIR', tmpDir.file('sub'))
+
+        expect:
+        def path = factory.path(tmpDir.file('sub', 'a'))
+        path.url == 'file://$SUB_DIR$/a'
+    }
+
+    def createsPathForARootDir() {
+        factory.addPathVariable('SUB_DIR', tmpDir.file('sub'))
+        factory.addPathVariable('ROOT_DIR', tmpDir.dir)
+
+        expect:
+        def path = factory.path(tmpDir.dir)
+        path.url == 'file://$ROOT_DIR$/'
+
+        path = factory.path(tmpDir.file('sub'))
+        path.url == 'file://$SUB_DIR$/'
+    }
+
+    def createsPathForAUrl() {
+        expect:
+        def path = factory.path('file://a/b/c')
+        path.url == 'file://a/b/c'
+    }
+}
diff --git a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/PathTest.groovy b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/PathTest.groovy
new file mode 100644
index 0000000..44390ef
--- /dev/null
+++ b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/PathTest.groovy
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.plugins.idea.model
+
+import org.gradle.util.Matchers
+import org.gradle.util.TemporaryFolder
+import org.junit.Rule
+import spock.lang.Specification
+
+class PathTest extends Specification {
+    @Rule TemporaryFolder tmpDir = new TemporaryFolder()
+
+    def generatesUrlAndPathForFileInRootDir() {
+        expect:
+        def path = new Path(tmpDir.dir, '$ROOT_DIR$', tmpDir.file('a', 'b'))
+        path.url == 'file://$ROOT_DIR$/a/b'
+        path.relPath == '$ROOT_DIR$/a/b'
+    }
+
+    def generatesUrlAndPathForRootDir() {
+        expect:
+        def path = new Path(tmpDir.dir, '$ROOT_DIR$', tmpDir.dir)
+        path.url == 'file://$ROOT_DIR$/'
+        path.relPath == '$ROOT_DIR$/'
+    }
+
+    def generatesUrlAndPathForAncestorOfRootDir() {
+        expect:
+        def path = new Path(tmpDir.dir, '$ROOT_DIR$', tmpDir.dir.parentFile.parentFile)
+        path.url == 'file://$ROOT_DIR$/../../'
+        path.relPath == '$ROOT_DIR$/../../'
+    }
+
+    def generatesUrlAndPathForSiblingOfRootDir() {
+        expect:
+        def path = new Path(tmpDir.dir, '$ROOT_DIR$', tmpDir.dir.parentFile.file('a'))
+        path.url == 'file://$ROOT_DIR$/../a'
+        path.relPath == '$ROOT_DIR$/../a'
+    }
+
+    def generatesUrlAndPathForJarFileInRootDir() {
+        expect:
+        def path = new Path(tmpDir.dir, '$ROOT_DIR$', tmpDir.file('a', 'b.jar'))
+        path.url == 'jar://$ROOT_DIR$/a/b.jar!/'
+        path.relPath == '$ROOT_DIR$/a/b.jar'
+    }
+
+    def generatesUrlAndPathForFileWithNoRootDir() {
+        def file = tmpDir.file('a')
+        def relpath = file.absolutePath.replace(File.separator, '/')
+
+        expect:
+        def path = new Path(file)
+        path.url == "file://${relpath}"
+        path.relPath == relpath
+    }
+
+    def generatesUrlAndPathForFileOnDifferentFilesystemToRootDir() {
+        def fileSystemRoots = findFileSystemRoots()
+        if (fileSystemRoots.size() == 1) {
+            return
+        }
+        def rootDir = new File(fileSystemRoots[0], 'root')
+        def file = new File(fileSystemRoots[1], 'file')
+        def relpath = file.absolutePath.replace(File.separator, '/')
+
+        expect:
+        def path = new Path(rootDir, '$ROOT_DIR$', file)
+        path.url == "file://${relpath}"
+        path.relPath == relpath
+    }
+
+    def generatesUrlAndPathForJarFileWithNoRootDir() {
+        def file = tmpDir.file('a.jar')
+        def relpath = file.absolutePath.replace(File.separator, '/')
+
+        expect:
+        def path = new Path(file)
+        path.url == "jar://${relpath}!/"
+        path.relPath == relpath
+    }
+
+    def pathsAreEqualWhenTheyHaveTheSameUrl() {
+        expect:
+        Matchers.strictlyEquals(new Path(tmpDir.dir, '$ROOT_DIR$', tmpDir.file('file')), new Path('file://$ROOT_DIR$/file'))
+        new Path('file://$ROOT_DIR$/file') != new Path('file://$ROOT_DIR$/other')
+    }
+
+    def findFileSystemRoots() {
+        File.listRoots().inject([]) {List result, File root ->
+            try {
+                new File(root, 'file').canonicalFile
+                result << root
+            } catch (IOException e) {
+                // skip
+            }
+            return result
+        }
+    }
+}
diff --git a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.groovy b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.groovy
index 4fedfdb..e8e1261 100644
--- a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.groovy
+++ b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/ProjectTest.groovy
@@ -16,7 +16,8 @@
 package org.gradle.plugins.idea.model
 
 import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.internal.XmlTransformer
 import spock.lang.Specification
 
 /**
@@ -69,10 +70,9 @@ class ProjectTest extends Specification {
     }
 
     def beforeConfigured() {
-        ListenerBroadcast beforeConfiguredActions = new ListenerBroadcast(Action)
-        beforeConfiguredActions.add("execute") { Project ideaProject ->
+        Action beforeConfiguredActions = { Project ideaProject ->
             ideaProject.modulePaths.clear()
-        }
+        } as Action
         def modulePaths = [new ModulePath("a", "b")] as Set
 
         when:
@@ -86,16 +86,14 @@ class ProjectTest extends Specification {
         def moduleFromInitialXml = null
         def moduleFromProjectConstructor = new ModulePath("a", "b")
         def moduleAddedInWhenConfiguredAction = new ModulePath("c", "d")
-        ListenerBroadcast beforeConfiguredActions = new ListenerBroadcast(Action)
-        beforeConfiguredActions.add("execute") { Project ideaProject ->
+        Action beforeConfiguredActions = { Project ideaProject ->
             moduleFromInitialXml = (ideaProject.modulePaths as List)[0]
-        }
-        ListenerBroadcast whenConfiguredActions = new ListenerBroadcast(Action)
-        whenConfiguredActions.add("execute") { Project ideaProject ->
+        } as Action
+        Action whenConfiguredActions = { Project ideaProject ->
             assert ideaProject.modulePaths.contains(moduleFromInitialXml)
             assert ideaProject.modulePaths.contains(moduleFromProjectConstructor)
             ideaProject.modulePaths.add(moduleAddedInWhenConfiguredAction)
-        }
+        } as Action
 
         when:
         project = createProject(modulePaths: [moduleFromProjectConstructor] as Set, reader: customProjectReader,
@@ -113,12 +111,13 @@ class ProjectTest extends Specification {
     }
 
     def withXml() {
-        ListenerBroadcast withXmlActions = new ListenerBroadcast(Action)
+        XmlTransformer withXmlActions = new XmlTransformer()
         project = createProject(reader: customProjectReader, withXmlActions: withXmlActions)
 
         when:
         def modifiedVersion
-        withXmlActions.add("execute") { xml ->
+        withXmlActions.addAction { XmlProvider provider ->
+            def xml = provider.asNode()
             xml. at version += 'x'
             modifiedVersion = xml. at version
         }
@@ -132,9 +131,10 @@ class ProjectTest extends Specification {
     }
 
     private Project createProject(Map customArgs) {
-        ListenerBroadcast dummyBroadcast = new ListenerBroadcast(Action)
+        Action dummyBroadcast = Mock()
+        XmlTransformer xmlTransformer = new XmlTransformer()
         Map args = [modulePaths: [] as Set, javaVersion: "1.6", wildcards: [] as Set, reader: null,
-                beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: dummyBroadcast] + customArgs
+                beforeConfiguredActions: dummyBroadcast, whenConfiguredActions: dummyBroadcast, withXmlActions: xmlTransformer] + customArgs
         return new Project(args.modulePaths, args.javaVersion, args.wildcards, args.reader,
                 args.beforeConfiguredActions, args.whenConfiguredActions, args.withXmlActions)
     }
diff --git a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/WorkspaceTest.groovy b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/WorkspaceTest.groovy
index 80c735c..7612965 100644
--- a/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/WorkspaceTest.groovy
+++ b/subprojects/gradle-idea/src/test/groovy/org/gradle/plugins/idea/model/WorkspaceTest.groovy
@@ -15,8 +15,8 @@
  */
 package org.gradle.plugins.idea.model
 
-import org.gradle.api.Action
-import org.gradle.listener.ListenerBroadcast
+import org.gradle.api.artifacts.maven.XmlProvider
+import org.gradle.api.internal.XmlTransformer
 import spock.lang.Specification
 
 /**
@@ -49,12 +49,13 @@ class WorkspaceTest extends Specification {
     }
 
     def withXml() {
-        ListenerBroadcast withXmlActions = new ListenerBroadcast(Action)
+        XmlTransformer withXmlActions = new XmlTransformer()
         workspace = createWorkspace(reader: customWorkspaceReader, withXmlActions: withXmlActions)
 
         when:
         def modifiedVersion
-        withXmlActions.add("execute") { xml ->
+        withXmlActions.addAction { XmlProvider provider ->
+            def xml = provider.asNode()
             xml. at version += 'x'
             modifiedVersion = xml. at version
         }
@@ -72,7 +73,7 @@ class WorkspaceTest extends Specification {
     }
 
     private Workspace createWorkspace(Map customArgs) {
-        ListenerBroadcast dummyBroadcast = new ListenerBroadcast(Action)
+        XmlTransformer dummyBroadcast = new XmlTransformer()
         Map args = [reader: null, withXmlActions: dummyBroadcast] + customArgs
         return new Workspace(args.reader, args.withXmlActions)
     }
diff --git a/subprojects/gradle-jetty/jetty.gradle b/subprojects/gradle-jetty/jetty.gradle
index 2278a0f..d48c65f 100644
--- a/subprojects/gradle-jetty/jetty.gradle
+++ b/subprojects/gradle-jetty/jetty.gradle
@@ -21,13 +21,13 @@ dependencies {
 
     compile libraries.slf4j_api,
             libraries.jetty_depends,
-            "org.mortbay.jetty:jetty-plus:6.1.22 at jar"
+            "org.mortbay.jetty:jetty-plus:6.1.25 at jar"
 
     runtime "org.mortbay.jetty:jsp-api-2.1:6.1.14 at jar",
             "org.mortbay.jetty:jsp-2.1:6.1.14 at jar",
             "org.eclipse.jdt:core:3.1.1 at jar",
-            "org.mortbay.jetty:jetty-naming:6.1.22 at jar",
-            "org.mortbay.jetty:jetty-annotations:6.1.22 at jar"
+            "org.mortbay.jetty:jetty-naming:6.1.25 at jar",
+            "org.mortbay.jetty:jetty-annotations:6.1.25 at jar"
 
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunTask.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunTask.java
index cb06120..6c15bc9 100644
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunTask.java
+++ b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunTask.java
@@ -36,18 +36,21 @@ import java.io.File;
 import java.net.URLClassLoader;
 import java.util.*;
 
+/**
+ * Base class for all tasks which deploy a web application to an embedded Jetty web container.
+ */
 public abstract class AbstractJettyRunTask extends ConventionTask {
     private static Logger logger = LoggerFactory.getLogger(AbstractJettyRunTask.class);
 
     private Iterable<File> additionalRuntimeJars = new ArrayList<File>();
 
     /**
-     * The proxy for the Server object
+     * The proxy for the Server object.
      */
     private JettyPluginServer server;
 
     /**
-     * The "virtual" webapp created by the plugin
+     * The "virtual" webapp created by the plugin.
      */
     private JettyPluginWebAppContext webAppConfig;
 
@@ -86,14 +89,12 @@ public abstract class AbstractJettyRunTask extends ConventionTask {
     private File jettyConfig;
 
     /**
-     * Port to listen to stop jetty on executing -DSTOP.PORT=<stopPort> -DSTOP.KEY=<stopKey> -jar start.jar
-     * --stop
+     * Port to listen to stop jetty on.
      */
     private Integer stopPort;
 
     /**
-     * Key to provide when stopping jetty on executing java -DSTOP.KEY=<stopKey> -DSTOP.PORT=<stopPort> -jar
-     * start.jar --stop
+     * Key to provide when stopping jetty.
      */
     private String stopKey;
 
@@ -124,17 +125,17 @@ public abstract class AbstractJettyRunTask extends ConventionTask {
     private RequestLog requestLog;
 
     /**
-     * A scanner to check for changes to the webapp
+     * A scanner to check for changes to the webapp.
      */
     private Scanner scanner = new Scanner();
 
     /**
-     * List of Listeners for the scanner
+     * List of Listeners for the scanner.
      */
     protected ArrayList scannerListeners;
 
     /**
-     * A scanner to check ENTER hits on the console
+     * A scanner to check ENTER hits on the console.
      */
     protected Thread consoleScanner;
 
@@ -147,7 +148,7 @@ public abstract class AbstractJettyRunTask extends ConventionTask {
     public abstract void applyJettyXml() throws Exception;
 
     /**
-     * create a proxy that wraps a particular jetty version Server object
+     * create a proxy that wraps a particular jetty version Server object.
      *
      * @return The Jetty Plugin Server
      */
@@ -273,7 +274,7 @@ public abstract class AbstractJettyRunTask extends ConventionTask {
     public abstract void restartWebApp(boolean reconfigureScanner) throws Exception;
 
     /**
-     * Subclasses should invoke this to setup basic info on the webapp
+     * Subclasses should invoke this to setup basic info on the webapp.
      */
     public void configureWebApplication() throws Exception {
         //use EITHER a <webAppConfig> element or the now deprecated <contextPath>, <webDefaultXml>, <overrideWebXml>
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunWarTask.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunWarTask.java
deleted file mode 100644
index e8fdd3f..0000000
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/AbstractJettyRunWarTask.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.plugins.jetty;
-
-import org.gradle.api.plugins.jetty.internal.Jetty6PluginServer;
-import org.mortbay.xml.XmlConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class AbstractJettyRunWarTask extends AbstractJettyRunTask {
-    private static Logger logger = LoggerFactory.getLogger(AbstractJettyRunWarTask.class);
-
-    public void applyJettyXml() throws Exception {
-
-        if (getJettyConfig() == null) {
-            return;
-        }
-
-        logger.info("Configuring Jetty from xml configuration file = {}", getJettyConfig());
-        XmlConfiguration xmlConfiguration = new XmlConfiguration(getJettyConfig().toURI().toURL());
-        xmlConfiguration.configure(getServer().getProxiedObject());
-    }
-
-
-    /**
-     * @see AbstractJettyRunTask#createServer()
-     */
-    public org.gradle.api.plugins.jetty.internal.JettyPluginServer createServer() throws Exception {
-        return new Jetty6PluginServer();
-    }
-
-
-
-}
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPluginConvention.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPluginConvention.java
index 8e95c22..bff4f35 100644
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPluginConvention.java
+++ b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyPluginConvention.java
@@ -16,6 +16,8 @@
 package org.gradle.api.plugins.jetty;
 
 /**
+ * Convention properties and methods added by the {@link org.gradle.api.plugins.jetty.JettyPlugin}.
+ *
  * @author Hans Dockter
  */
 public class JettyPluginConvention {
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRun.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRun.java
index 32e4101..d896b15 100644
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRun.java
+++ b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRun.java
@@ -42,8 +42,8 @@ import java.io.IOException;
 import java.util.*;
 
 /**
- * <p>The {@code JettyRun} task deploys an exploded web application to an embedded Jetty web container, without first
- * requiring that the web application be assembled into a war, saving time during the development cycle.</p>
+ * <p>Deploys an exploded web application to an embedded Jetty web container. Does not require that the web application
+ * be assembled into a war, saving time during the development cycle.</p>
  *
  * <p>Once started, the web container can be configured to run continuously, scanning for changes in the project and
  * automatically performing a hot redeploy when necessary. This allows the developer to concentrate on coding changes to
@@ -71,7 +71,7 @@ public class JettyRun extends AbstractJettyRunTask {
     private File webXml;
 
     /**
-     * Root directory for all html/jsp etc files
+     * Root directory for all html/jsp etc files.
      */
     private File webAppSourceDirectory;
 
@@ -87,17 +87,17 @@ public class JettyRun extends AbstractJettyRunTask {
     private ScanTargetPattern[] scanTargetPatterns;
 
     /**
-     * jetty-env.xml as a File
+     * jetty-env.xml as a File.
      */
     private File jettyEnvXmlFile;
 
     /**
-     * List of files on the classpath for the webapp
+     * List of files on the classpath for the webapp.
      */
     private List<File> classPathFiles;
 
     /**
-     * Extra scan targets as a list
+     * Extra scan targets as a list.
      */
     private List<File> extraScanTargets;
 
@@ -284,8 +284,8 @@ public class JettyRun extends AbstractJettyRunTask {
                         int size = old.length + overlays.size();
                         Resource[] resources = new Resource[size];
                         System.arraycopy(old, 0, resources, 0, old.length);
-                        for (int i = old.length, j = 0; i < size; i++, j++) {
-                            resources[i] = overlays.get(j);
+                        for (int i = old.length; i < size; i++) {
+                            resources[i] = overlays.get(i - old.length);
                             logger.info("Adding overlay: " + resources[i]);
                         }
                         rc.setResources(resources);
@@ -351,9 +351,6 @@ public class JettyRun extends AbstractJettyRunTask {
         xmlConfiguration.configure(getServer().getProxiedObject());
     }
 
-    /**
-     * @see JettyRun#createServer()
-     */
     public JettyPluginServer createServer() {
         return new Jetty6PluginServer();
     }
@@ -429,9 +426,6 @@ public class JettyRun extends AbstractJettyRunTask {
         this.scanTargetPatterns = scanTargetPatterns;
     }
 
-    /**
-     * @return Returns the contextHandlers.
-     */
     public ContextHandler[] getConfiguredContextHandlers() {
         return this.contextHandlers;
     }
@@ -449,7 +443,7 @@ public class JettyRun extends AbstractJettyRunTask {
     }
 
     /**
-     * Set the classpath for the web application
+     * Set the classpath for the web application.
      */
     public void setClasspath(FileCollection classpath) {
         this.classpath = classpath;
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRunWar.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRunWar.java
index e2fc531..e75d793 100644
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRunWar.java
+++ b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyRunWar.java
@@ -16,7 +16,9 @@
 
 package org.gradle.api.plugins.jetty;
 
+import org.gradle.api.plugins.jetty.internal.Jetty6PluginServer;
 import org.mortbay.util.Scanner;
+import org.mortbay.xml.XmlConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.gradle.api.tasks.InputFile;
@@ -26,12 +28,12 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * <p>The {@code JettyRunWar} deploys a WAR to an embedded Jetty web container.</p>
+ * <p>Deploys a WAR to an embedded Jetty web container.</p>
  *
  * <p> Once started, the web container can be configured to run continuously, scanning for changes to the war file and
  * automatically performing a hot redeploy when necessary. </p>
  */
-public class JettyRunWar extends AbstractJettyRunWarTask {
+public class JettyRunWar extends AbstractJettyRunTask {
     private static Logger logger = LoggerFactory.getLogger(JettyRunWar.class);
 
     /**
@@ -47,9 +49,6 @@ public class JettyRunWar extends AbstractJettyRunWarTask {
     }
 
 
-    /**
-     * @see AbstractJettyRunTask#validateConfiguration()
-     */
     public void validateConfiguration() {
     }
 
@@ -100,9 +99,6 @@ public class JettyRunWar extends AbstractJettyRunWarTask {
     }
 
 
-    /**
-     * @see AbstractJettyRunTask#finishConfigurationBeforeStart()
-     */
     public void finishConfigurationBeforeStart() {
     }
 
@@ -114,4 +110,19 @@ public class JettyRunWar extends AbstractJettyRunWarTask {
     public void setWebApp(File webApp) {
         this.webApp = webApp;
     }
+
+    public void applyJettyXml() throws Exception {
+
+        if (getJettyConfig() == null) {
+            return;
+        }
+
+        logger.info("Configuring Jetty from xml configuration file = {}", getJettyConfig());
+        XmlConfiguration xmlConfiguration = new XmlConfiguration(getJettyConfig().toURI().toURL());
+        xmlConfiguration.configure(getServer().getProxiedObject());
+    }
+
+    public org.gradle.api.plugins.jetty.internal.JettyPluginServer createServer() throws Exception {
+        return new Jetty6PluginServer();
+    }
 }
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyStop.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyStop.java
index fa213c0..b340add 100644
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyStop.java
+++ b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/JettyStop.java
@@ -27,6 +27,9 @@ import java.net.ConnectException;
 import java.net.InetAddress;
 import java.net.Socket;
 
+/**
+ * Stops the embedded Jetty web container.
+ */
 public class JettyStop extends ConventionTask {
     private static Logger logger = LoggerFactory.getLogger(JettyStop.class);
 
@@ -59,14 +62,14 @@ public class JettyStop extends ConventionTask {
     }
 
     /**
-     * Returns port to listen to stop jetty on sending stop command
+     * Returns port to listen to stop jetty on sending stop command.
      */
     public Integer getStopPort() {
         return stopPort;
     }
 
     /**
-     * Sets port to listen to stop jetty on sending stop command
+     * Sets port to listen to stop jetty on sending stop command.
      */
     public void setStopPort(Integer stopPort) {
         this.stopPort = stopPort;
@@ -82,8 +85,7 @@ public class JettyStop extends ConventionTask {
     }
 
     /**
-     * Sets key to provide when stopping jetty on executing java -DSTOP.KEY=<stopKey> -DSTOP.PORT=<stopPort>
-     * -jar start.jar --stop
+     * Sets key to provide when stopping jetty.
      */
     public void setStopKey(String stopKey) {
         this.stopKey = stopKey;
diff --git a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/ScanTargetPattern.java b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/ScanTargetPattern.java
index d7fe1b9..a3b5286 100644
--- a/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/ScanTargetPattern.java
+++ b/subprojects/gradle-jetty/src/main/java/org/gradle/api/plugins/jetty/ScanTargetPattern.java
@@ -20,23 +20,20 @@ import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
+/**
+ * Patterns for scanning for file changes.
+ */
 public class ScanTargetPattern
 {
     private File directory;
     private List includes = Collections.EMPTY_LIST;
     private List excludes = Collections.EMPTY_LIST;
 
-    /**
-     * @return the _directory
-     */
     public File getDirectory()
     {
         return directory;
     }
 
-    /**
-     * @param directory the _directory to set
-     */
     public void setDirectory(File directory)
     {
         this.directory = directory;
diff --git a/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/BuildCompleter.java b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/BuildCompleter.java
new file mode 100644
index 0000000..d8bb8b6
--- /dev/null
+++ b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/BuildCompleter.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher;
+
+public interface BuildCompleter {
+    void exit(Throwable failure);
+}
diff --git a/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java
new file mode 100644
index 0000000..b400da0
--- /dev/null
+++ b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/CommandLineActionFactory.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher;
+
+import org.gradle.*;
+import org.gradle.api.internal.project.ServiceRegistry;
+import org.gradle.configuration.GradleLauncherMetaData;
+import org.gradle.gradleplugin.userinterface.swing.standalone.BlockingApplication;
+import org.gradle.initialization.*;
+import org.gradle.logging.LoggingConfiguration;
+import org.gradle.logging.LoggingManagerInternal;
+import org.gradle.logging.LoggingServiceRegistry;
+import org.gradle.util.GradleVersion;
+
+import java.io.PrintStream;
+import java.util.List;
+
+/**
+ * Responsible for converting a set of command-line arguments into a {@link Runnable} action.
+ */
+public class CommandLineActionFactory {
+    private static final String HELP = "h";
+    private static final String GUI = "gui";
+    private static final String VERSION = "v";
+    private final BuildCompleter buildCompleter;
+
+    public CommandLineActionFactory(BuildCompleter buildCompleter) {
+        this.buildCompleter = buildCompleter;
+    }
+
+    /**
+     * Converts the given command-line arguments to a {@code Runnable} action which performs the action requested by the
+     * command-line args. Does not have any side-effects. Each action will call the supplied {@link
+     * org.gradle.launcher.BuildCompleter} once it has completed.
+     *
+     * @param args The command-line arguments.
+     * @return The action to execute.
+     */
+    Runnable convert(List<String> args) {
+        CommandLineParser parser = new CommandLineParser();
+
+        CommandLineConverter<StartParameter> startParameterConverter = createStartParameterConverter();
+        startParameterConverter.configure(parser);
+
+        parser.option(HELP, "?", "help").hasDescription("Shows this help message");
+        parser.option(VERSION, "version").hasDescription("Print version info.");
+        parser.option(GUI).hasDescription("Launches a GUI application");
+
+        LoggingConfiguration loggingConfiguration = new LoggingConfiguration();
+        ServiceRegistry loggingServices = createLoggingServices();
+
+        Runnable action;
+        try {
+            ParsedCommandLine commandLine = parser.parse(args);
+            CommandLineConverter<LoggingConfiguration> loggingConfigurationConverter = loggingServices.get(CommandLineConverter.class);
+            loggingConfiguration = loggingConfigurationConverter.convert(commandLine);
+            action = createAction(parser, commandLine, startParameterConverter, loggingServices);
+        } catch (CommandLineArgumentException e) {
+            action = new CommandLineParseFailureAction(parser, e);
+        }
+
+        return new WithLoggingAction(loggingConfiguration, loggingServices, action);
+    }
+
+    CommandLineConverter<StartParameter> createStartParameterConverter() {
+        return new DefaultCommandLineConverter();
+    }
+
+    ServiceRegistry createLoggingServices() {
+        return new LoggingServiceRegistry();
+    }
+
+    GradleLauncherFactory createGradleLauncherFactory(ServiceRegistry loggingServices) {
+        return new DefaultGradleLauncherFactory(loggingServices);
+    }
+
+    private Runnable createAction(CommandLineParser parser, ParsedCommandLine commandLine, CommandLineConverter<StartParameter> startParameterConverter, ServiceRegistry loggingServices) {
+        if (commandLine.hasOption(HELP)) {
+            return new ShowUsageAction(parser);
+        }
+        if (commandLine.hasOption(VERSION)) {
+            return new ShowVersionAction();
+        }
+        if (commandLine.hasOption(GUI)) {
+            return new ShowGuiAction();
+        }
+
+        StartParameter startParameter = startParameterConverter.convert(commandLine);
+        return new RunBuildAction(startParameter, loggingServices);
+    }
+
+    private void showUsage(PrintStream out, CommandLineParser parser) {
+        out.println();
+        out.print("USAGE: ");
+        new GradleLauncherMetaData().describeCommand(out, "[option...]", "[task...]");
+        out.println();
+        out.println();
+        parser.printUsage(out);
+        out.println();
+    }
+
+    private class CommandLineParseFailureAction implements Runnable {
+        private final Exception e;
+        private final CommandLineParser parser;
+
+        public CommandLineParseFailureAction(CommandLineParser parser, Exception e) {
+            this.parser = parser;
+            this.e = e;
+        }
+
+        public void run() {
+            System.err.println();
+            System.err.println(e.getMessage());
+            showUsage(System.err, parser);
+            buildCompleter.exit(e);
+        }
+    }
+
+    private class ShowUsageAction implements Runnable {
+        private final CommandLineParser parser;
+
+        public ShowUsageAction(CommandLineParser parser) {
+            this.parser = parser;
+        }
+
+        public void run() {
+            showUsage(System.out, parser);
+            buildCompleter.exit(null);
+        }
+    }
+
+    private class ShowVersionAction implements Runnable {
+        public void run() {
+            System.out.println(new GradleVersion().prettyPrint());
+            buildCompleter.exit(null);
+        }
+    }
+
+    class ShowGuiAction implements Runnable {
+        public void run() {
+            BlockingApplication.launchAndBlock();
+            buildCompleter.exit(null);
+        }
+    }
+
+    private class RunBuildAction implements Runnable {
+        private final StartParameter startParameter;
+        private final ServiceRegistry loggingServices;
+
+        public RunBuildAction(StartParameter startParameter, ServiceRegistry loggingServices) {
+            this.startParameter = startParameter;
+            this.loggingServices = loggingServices;
+        }
+
+        public void run() {
+            GradleLauncherFactory gradleLauncherFactory = createGradleLauncherFactory(loggingServices);
+            GradleLauncher gradleLauncher = gradleLauncherFactory.newInstance(startParameter);
+            BuildResult buildResult = gradleLauncher.run();
+            buildCompleter.exit(buildResult.getFailure());
+        }
+    }
+
+    class WithLoggingAction implements Runnable {
+        private final LoggingConfiguration loggingConfiguration;
+        private final ServiceRegistry loggingServices;
+        private final Runnable action;
+
+        public WithLoggingAction(LoggingConfiguration loggingConfiguration, ServiceRegistry loggingServices, Runnable action) {
+            this.loggingConfiguration = loggingConfiguration;
+            this.loggingServices = loggingServices;
+            this.action = action;
+        }
+
+        public void run() {
+            LoggingManagerInternal loggingManager = loggingServices.getFactory(LoggingManagerInternal.class).create();
+            loggingManager.setLevel(loggingConfiguration.getLogLevel());
+            loggingManager.colorStdOutAndStdErr(loggingConfiguration.isColorOutput());
+            loggingManager.start();
+            action.run();
+        }
+    }
+}
diff --git a/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/GradleMain.java b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/GradleMain.java
index 0d05514..e30b620 100644
--- a/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/GradleMain.java
+++ b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/GradleMain.java
@@ -36,15 +36,18 @@ public class GradleMain {
         processGradleUserHome(bootStrapDebug);
 
         ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry();
-        URL[] classpath = classPathRegistry.getClassPathUrls("GRADLE_RUNTIME");
-        ClassLoader parentClassloader = ClassLoader.getSystemClassLoader().getParent();
+        URL[] antClasspath = classPathRegistry.getClassPathUrls("ANT");
+        URL[] runtimeClasspath = classPathRegistry.getClassPathUrls("GRADLE_RUNTIME");
+        ClassLoader rootClassLoader = ClassLoader.getSystemClassLoader().getParent();
         if (bootStrapDebug) {
-            System.out.println("Parent Classloader of new context classloader is: " + parentClassloader);
-            System.out.println("Adding the following files to new lib classloader: " + Arrays.toString(classpath));
+            System.out.println("Parent Classloader of new context classloader is: " + rootClassLoader);
+            System.out.println("Using Ant classpath: " + Arrays.toString(antClasspath));
+            System.out.println("Using runtime classpath: " + Arrays.toString(runtimeClasspath));
         }
-        URLClassLoader libClassLoader = new URLClassLoader(classpath, parentClassloader);
-        Thread.currentThread().setContextClassLoader(libClassLoader);
-        Class mainClass = libClassLoader.loadClass("org.gradle.launcher.Main");
+        URLClassLoader antClassLoader = new URLClassLoader(antClasspath, rootClassLoader);
+        URLClassLoader runtimeClassLoader = new URLClassLoader(runtimeClasspath, antClassLoader);
+        Thread.currentThread().setContextClassLoader(runtimeClassLoader);
+        Class mainClass = runtimeClassLoader.loadClass("org.gradle.launcher.Main");
         Method mainMethod = mainClass.getMethod("main", String[].class);
         mainMethod.invoke(null, new Object[]{args});
     }
diff --git a/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/Main.java b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/Main.java
index 28d79e2..f66e164 100644
--- a/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/Main.java
+++ b/subprojects/gradle-launcher/src/main/java/org/gradle/launcher/Main.java
@@ -15,94 +15,53 @@
  */
 package org.gradle.launcher;
 
-import org.gradle.*;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.gradleplugin.userinterface.swing.standalone.BlockingApplication;
-import org.gradle.initialization.CommandLine2StartParameterConverter;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
-import org.gradle.util.Clock;
-import org.gradle.util.GradleVersion;
+import org.gradle.BuildExceptionReporter;
+import org.gradle.StartParameter;
+import org.gradle.logging.internal.StreamingStyledTextOutputFactory;
+
+import java.util.Arrays;
 
 /**
+ * The main command-line entry-point for Gradle.
+ *
  * @author Hans Dockter
  */
 public class Main {
-    private static Logger logger = Logging.getLogger(Main.class);
-
     private final String[] args;
-    private BuildCompleter buildCompleter = new ProcessExitBuildCompleter();
-    private CommandLine2StartParameterConverter parameterConverter = new DefaultCommandLine2StartParameterConverter();
 
     public Main(String[] args) {
         this.args = args;
     }
 
-    public static void main(String[] args) throws Throwable {
-        new Main(args).execute();
-    }
-
-    void setBuildCompleter(BuildCompleter buildCompleter) {
-        this.buildCompleter = buildCompleter;
-    }
-
-    public void setParameterConverter(CommandLine2StartParameterConverter parameterConverter) {
-        this.parameterConverter = parameterConverter;
-    }
-
-    public void execute() throws Exception {
-        Clock buildTimeClock = new Clock();
-
-        StartParameter startParameter = null;
-
+    public static void main(String[] args) {
         try {
-            startParameter = parameterConverter.convert(args);
-        } catch (Exception e) {
-            System.err.println(e.getMessage());
-            parameterConverter.showHelp(System.err);
-            buildCompleter.exit(e);
-        }
-
-        if (startParameter.isShowHelp()) {
-            parameterConverter.showHelp(System.out);
-            buildCompleter.exit(null);
-        }
-
-        if (startParameter.isShowVersion()) {
-            System.out.println(new GradleVersion().prettyPrint());
-            buildCompleter.exit(null);
-        }
-
-        if (startParameter.isLaunchGUI()) {
-            try {
-                BlockingApplication.launchAndBlock();
-            } catch (Throwable e) {
-                logger.error("Failed to run the UI.", e);
-                buildCompleter.exit(e);
-            }
-
-            buildCompleter.exit(null);
+            new Main(args).execute();
+            System.exit(0);
+        } catch (Throwable throwable) {
+            throwable.printStackTrace();
+            System.exit(1);
         }
+    }
 
-        BuildListener resultLogger = new BuildLogger(logger, buildTimeClock, startParameter);
+    public void execute() {
+        BuildCompleter buildCompleter = createBuildCompleter();
         try {
-            GradleLauncher gradleLauncher = GradleLauncher.newInstance(startParameter);
-
-            gradleLauncher.useLogger(resultLogger);
-
-            BuildResult buildResult = gradleLauncher.run();
-            if (buildResult.getFailure() != null) {
-                buildCompleter.exit(buildResult.getFailure());
-            }
+            CommandLineActionFactory actionFactory = createActionFactory(buildCompleter);
+            Runnable action = actionFactory.convert(Arrays.asList(args));
+            action.run();
+            buildCompleter.exit(null);
         } catch (Throwable e) {
-            resultLogger.buildFinished(new BuildResult(null, e));
+            new BuildExceptionReporter(new StreamingStyledTextOutputFactory(System.err), new StartParameter()).reportException(e);
             buildCompleter.exit(e);
         }
-        buildCompleter.exit(null);
     }
 
-    public interface BuildCompleter {
-        void exit(Throwable failure);
+    CommandLineActionFactory createActionFactory(BuildCompleter buildCompleter) {
+        return new CommandLineActionFactory(buildCompleter);
+    }
+
+    BuildCompleter createBuildCompleter() {
+        return new ProcessExitBuildCompleter();
     }
 
     private static class ProcessExitBuildCompleter implements BuildCompleter {
diff --git a/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy b/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy
new file mode 100644
index 0000000..5cadf85
--- /dev/null
+++ b/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/CommandLineActionFactoryTest.groovy
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher
+
+import org.gradle.initialization.CommandLineConverter
+import org.gradle.util.GradleVersion
+import org.gradle.util.RedirectStdOutAndErr
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+import org.gradle.*
+import org.gradle.api.internal.project.ServiceRegistry
+import org.gradle.logging.LoggingConfiguration
+import org.gradle.logging.LoggingManagerInternal
+import org.gradle.api.internal.Factory
+
+class CommandLineActionFactoryTest extends Specification {
+    @Rule
+    public final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr();
+    @Rule
+    public final SetSystemProperties sysProperties = new SetSystemProperties();
+    final BuildCompleter buildCompleter = Mock()
+    final CommandLineConverter<StartParameter> startParameterConverter = Mock()
+    final GradleLauncherFactory gradleLauncherFactory = Mock()
+    final GradleLauncher gradleLauncher = Mock()
+    final BuildResult buildResult = Mock()
+    final ServiceRegistry loggingServices = Mock()
+    final CommandLineConverter<LoggingConfiguration> loggingConfigurationConverter = Mock()
+    final LoggingManagerInternal loggingManager = Mock()
+    final CommandLineActionFactory factory = new CommandLineActionFactory(buildCompleter) {
+        @Override
+        ServiceRegistry createLoggingServices() {
+            return loggingServices
+        }
+
+        @Override
+        CommandLineConverter<StartParameter> createStartParameterConverter() {
+            return startParameterConverter
+        }
+
+        @Override
+        GradleLauncherFactory createGradleLauncherFactory(ServiceRegistry loggingServices) {
+            return gradleLauncherFactory
+        }
+    }
+
+    def setup() {
+        _ * loggingServices.get(CommandLineConverter) >> loggingConfigurationConverter
+        _ * loggingConfigurationConverter.convert(!null) >> new LoggingConfiguration()
+        Factory<LoggingManagerInternal> loggingManagerFactory = Mock()
+        _ * loggingServices.getFactory(LoggingManagerInternal) >> loggingManagerFactory
+        _ * loggingManagerFactory.create() >> loggingManager
+    }
+
+    def reportsCommandLineParseFailure() {
+        def failure = new CommandLineArgumentException('<broken>')
+
+        when:
+        def action = factory.convert([])
+
+        then:
+        1 * startParameterConverter.configure(!null) >> { args -> args[0].option('some-build-option') }
+        1 * startParameterConverter.convert(!null) >> { throw failure }
+
+        when:
+        action.run()
+
+        then:
+        1 * loggingManager.start()
+        outputs.stdErr.contains('<broken>')
+        outputs.stdErr.contains('USAGE: gradle [option...] [task...]')
+        outputs.stdErr.contains('--help')
+        outputs.stdErr.contains('--some-build-option')
+        1 * buildCompleter.exit(failure)
+    }
+
+    def displaysUsageMessage() {
+        when:
+        def action = factory.convert([option])
+        action.run()
+
+        then:
+        _ * startParameterConverter.configure(!null) >> { args -> args[0].option('some-build-option') }
+        1 * loggingManager.start()
+        outputs.stdOut.contains('USAGE: gradle [option...] [task...]')
+        outputs.stdOut.contains('--help')
+        outputs.stdOut.contains('--some-build-option')
+        1 * buildCompleter.exit(null)
+
+        where:
+        option << ['-h', '-?', '--help']
+    }
+
+    def usesSystemPropertyForGradleAppName() {
+        System.setProperty("org.gradle.appname", "gradle-app");
+
+        when:
+        def action = factory.convert(['-?'])
+        action.run()
+
+        then:
+        outputs.stdOut.contains('USAGE: gradle-app [option...] [task...]')
+        1 * buildCompleter.exit(null)
+    }
+
+    def displaysVersionMessage() {
+        when:
+        def action = factory.convert([option])
+        action.run()
+
+        then:
+        1 * loggingManager.start()
+        outputs.stdOut.contains(new GradleVersion().prettyPrint())
+        1 * buildCompleter.exit(null)
+
+        where:
+        option << ['-v', '--version']
+    }
+
+    def launchesGUI() {
+        when:
+        def action = factory.convert(['--gui'])
+
+        then:
+        action instanceof CommandLineActionFactory.WithLoggingAction
+        action.action instanceof CommandLineActionFactory.ShowGuiAction
+    }
+
+    def executesBuild() {
+        def startParameter = new StartParameter();
+
+        when:
+        def action = factory.convert(['args'])
+
+        then:
+        1 * startParameterConverter.convert(!null) >> startParameter
+
+        when:
+        action.run()
+
+        then:
+        1 * loggingManager.start()
+        1 * gradleLauncherFactory.newInstance(startParameter) >> gradleLauncher
+        1 * gradleLauncher.run() >> buildResult
+        1 * buildResult.failure >> null
+        1 * buildCompleter.exit(null)
+    }
+
+    def executesFailedBuild() {
+        def RuntimeException failure = new RuntimeException()
+        def startParameter = new StartParameter();
+
+        when:
+        def action = factory.convert(['args'])
+
+        then:
+        1 * startParameterConverter.convert(!null) >> startParameter
+
+        when:
+        action.run()
+
+        then:
+        1 * loggingManager.start()
+        1 * gradleLauncherFactory.newInstance(startParameter) >> gradleLauncher
+        1 * gradleLauncher.run() >> buildResult
+        1 * buildResult.failure >> failure
+        1 * buildCompleter.exit(failure)
+    }
+}
diff --git a/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy b/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy
new file mode 100644
index 0000000..d3716a2
--- /dev/null
+++ b/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/MainTest.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.launcher
+
+import spock.lang.Specification
+import org.junit.Rule
+import org.gradle.util.RedirectStdOutAndErr
+
+class MainTest extends Specification {
+    @Rule final RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
+    final BuildCompleter completer = Mock()
+    final CommandLineActionFactory factory = Mock()
+    final String[] args = ['arg']
+    final Main main = new Main(args) {
+        @Override
+        BuildCompleter createBuildCompleter() {
+            completer
+        }
+
+        @Override
+        CommandLineActionFactory createActionFactory(BuildCompleter buildCompleter) {
+            assert buildCompleter == completer
+            factory
+        }
+    }
+
+    def createsAndExecutesCommandLineAction() {
+        Runnable action = Mock()
+
+        when:
+        main.execute()
+
+        then:
+        1 * factory.convert(args) >> action
+        1 * action.run()
+        1 * completer.exit(null)
+        0 * factory._
+        0 * action._
+        0 * completer._
+    }
+
+    def reportsActionExecutionFailure() {
+        Runnable action = Mock()
+        RuntimeException failure = new RuntimeException('broken')
+
+        when:
+        main.execute()
+
+        then:
+        1 * factory.convert(args) >> action
+        1 * action.run() >> { throw failure }
+        outputs.stdErr.contains('internal error')
+        outputs.stdErr.contains('java.lang.RuntimeException: broken')
+        1 * completer.exit(failure)
+        0 * factory._
+        0 * action._
+        0 * completer._
+    }
+
+    def reportsActionCreationFailure() {
+        RuntimeException failure = new RuntimeException('broken')
+
+        when:
+        main.execute()
+
+        then:
+        1 * factory.convert(args) >> { throw failure }
+        outputs.stdErr.contains('internal error')
+        outputs.stdErr.contains('java.lang.RuntimeException: broken')
+        1 * completer.exit(failure)
+        0 * factory._
+        0 * completer._
+    }
+}
diff --git a/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/MainTest.java b/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/MainTest.java
deleted file mode 100644
index 281a556..0000000
--- a/subprojects/gradle-launcher/src/test/groovy/org/gradle/launcher/MainTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.launcher;
-
-import org.gradle.*;
-import org.gradle.initialization.CommandLine2StartParameterConverter;
-import org.gradle.util.HelperUtil;
-import static org.hamcrest.Matchers.*;
-import org.jmock.Expectations;
-import org.jmock.integration.junit4.JMock;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import static org.junit.Assert.*;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * @author Hans Dockter
- */
- at RunWith(JMock.class)
-public class MainTest {
-    private static final String[] TEST_ARGS = { "arg1", "arg2" };
-    private JUnit4Mockery context = new JUnit4Mockery() {{
-        setImposteriser(ClassImposteriser.INSTANCE);
-    }};
-
-    private void setUpGradle(final BuildResult buildResult, final StartParameter startParameter) {
-        final GradleLauncher gradleMockLauncher = context.mock(GradleLauncher.class);
-        final GradleLauncherFactory gradleLauncherFactoryMock = context.mock(GradleLauncherFactory.class);
-
-        GradleLauncher.injectCustomFactory(gradleLauncherFactoryMock);
-        context.checking(new Expectations() {{
-            one(gradleLauncherFactoryMock).newInstance(startParameter); will(returnValue(gradleMockLauncher));
-            one(gradleMockLauncher).useLogger(with(any(BuildLogger.class)));
-            one(gradleMockLauncher).run(); will(returnValue(buildResult));
-        }});
-    }
-
-    private void setUpMain(Main main, final StartParameter startParameter) {
-        final CommandLine2StartParameterConverter commandLine2StartParameterConverterStub =
-                context.mock(CommandLine2StartParameterConverter.class);
-        main.setParameterConverter(commandLine2StartParameterConverterStub);
-        main.setBuildCompleter(new Main.BuildCompleter() {
-            public void exit(Throwable failure) {
-                throw new BuildCompletedError(failure);
-            }
-        });
-        context.checking(new Expectations() {{
-            allowing(commandLine2StartParameterConverterStub).convert(TEST_ARGS);
-            will(returnValue(startParameter));    
-        }});
-    }
-
-
-    @Test
-    public void runBuild() throws Exception {
-        Main main = new Main(TEST_ARGS);
-        BuildResult buildResult = HelperUtil.createBuildResult(null);
-        StartParameter startParameter = new StartParameter();
-        setUpMain(main, startParameter);
-        setUpGradle(buildResult, startParameter);
-        try {
-            main.execute();
-            fail();
-        } catch (BuildCompletedError e) {
-            assertThat(e.getCause(), nullValue());
-        } catch (Throwable t) {
-            t.printStackTrace();
-        }
-    }
-
-    @Test
-    public void runBuildWithFailure() throws Exception {
-        Main main = new Main(TEST_ARGS);
-        RuntimeException exception = new RuntimeException();
-        BuildResult buildResult = HelperUtil.createBuildResult(exception);
-        StartParameter startParameter = new StartParameter();
-        setUpMain(main, startParameter);
-        setUpGradle(buildResult, startParameter);
-        try {
-            main.execute();
-            fail();
-        } catch (BuildCompletedError e) {
-            assertThat((RuntimeException) (e.getCause().getCause()), sameInstance(exception));
-        }
-    }
-
-    @Test
-    public void showHelp() throws Exception {
-        final CommandLine2StartParameterConverter commandLine2StartParameterConverterMock =
-                context.mock(CommandLine2StartParameterConverter.class, "helpMock");
-        Main main = new Main(TEST_ARGS);
-        final StartParameter startParameter = new StartParameter();
-        startParameter.setShowHelp(true);
-        setUpMain(main, startParameter);
-        main.setParameterConverter(commandLine2StartParameterConverterMock);
-        context.checking(new Expectations() {{
-            one(commandLine2StartParameterConverterMock).convert(TEST_ARGS); will(returnValue(startParameter));
-            one(commandLine2StartParameterConverterMock).showHelp(System.out);
-        }});
-        try {
-            main.execute();
-            fail();
-        } catch (BuildCompletedError e) {
-            assertThat(e.getCause(), nullValue());
-        }
-    }
-
-    @Test
-    public void showVersion() throws Exception {
-        // This tests just that showVersion does not lead to running a build or throwing of an exception.
-        Main main = new Main(TEST_ARGS);
-        StartParameter startParameter = new StartParameter();
-        startParameter.setShowVersion(true);
-        setUpMain(main, startParameter);
-        try {
-            main.execute();
-            fail();
-        } catch (BuildCompletedError e) {
-            assertThat(e.getCause(), nullValue());
-        }
-    }
-
-    @Test
-    public void illegalCommandLineArgs() throws Exception {
-        final CommandLine2StartParameterConverter commandLine2StartParameterConverterMock =
-                context.mock(CommandLine2StartParameterConverter.class, "exceptionMock");
-        Main main = new Main(TEST_ARGS);
-        final CommandLineArgumentException conversionException = new CommandLineArgumentException("fail");
-        final StartParameter startParameter = new StartParameter();
-        setUpMain(main, startParameter);
-        main.setParameterConverter(commandLine2StartParameterConverterMock);
-        context.checking(new Expectations() {{
-            allowing(commandLine2StartParameterConverterMock).convert(TEST_ARGS); will(throwException(conversionException));
-            one(commandLine2StartParameterConverterMock).showHelp(System.err);
-        }});
-        try {
-            main.execute();
-            fail();
-        } catch (BuildCompletedError e) {
-            assertThat((CommandLineArgumentException) e.getCause(), sameInstance(conversionException));
-        }
-    }
-
-
-    private class BuildCompletedError extends Error {
-        public BuildCompletedError(Throwable failure) {
-            super(failure);
-        }
-    }
-}
-
diff --git a/subprojects/gradle-maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java b/subprojects/gradle-maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java
index 8456a7f..8e4fabd 100644
--- a/subprojects/gradle-maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java
+++ b/subprojects/gradle-maven/src/main/groovy/org/gradle/api/plugins/MavenPluginConvention.java
@@ -28,6 +28,8 @@ import org.gradle.util.ConfigureUtil;
 import java.io.File;
 
 /**
+ * Properties and methods added by the {@link org.gradle.api.plugins.MavenPlugin}.
+ * 
  * @author Hans Dockter
  */
 public class MavenPluginConvention {
diff --git a/subprojects/gradle-open-api/open-api.gradle b/subprojects/gradle-open-api/open-api.gradle
index 9fef018..7971c23 100644
--- a/subprojects/gradle-open-api/open-api.gradle
+++ b/subprojects/gradle-open-api/open-api.gradle
@@ -1,52 +1,23 @@
 apply plugin: 'groovy'
+apply from: "$rootDir/gradle/integTest.gradle"
 
 dependencies {
     groovy libraries.groovy_depends
+
     testCompile project(path: ':core', configuration: 'testFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
 
-    testCompile project(path: ':core', configuration: 'integTestFixtures')
-    testRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
-}
-
-//define our integration tests
-configurations {
-    integTestCompile {
-        extendsFrom testCompile
-    }
-    integTestRuntime {
-        extendsFrom integTestCompile, testRuntime
-    }
-}
-
-//define the source sets for our integration tests
-sourceSets {
-    integTest {
-        compileClasspath = sourceSets.test.classes + sourceSets.main.classes + configurations.integTestCompile + project(':ui').sourceSets.main.classes
-        runtimeClasspath = classes + compileClasspath + configurations.integTestRuntime
-    }
+    integTestCompile libraries.slf4j_api
+    integTestCompile project(path: ':core', configuration: 'integTestFixtures')
+    integTestRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
 }
 
 //create a task for running the integration tests
 task integTest(type: Test, dependsOn: [ rootProject.intTestImage ]) {
     systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath
-    systemProperties['integTest.srcDir'] = file('src').absolutePath
     systemProperties['integTest.gradleUserHomeDir'] = rootProject.integTest.integTestUserDir.absolutePath
     testClassesDir = sourceSets.integTest.classesDir
     classpath = sourceSets.integTest.runtimeClasspath + configurations.integTestRuntime
 
     doFirst { task -> systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath }
 }
-
-ideaModule {
-    testSourceDirs.addAll(project.sourceSets.integTest.groovy.srcDirs)
-    whenConfigured { module ->
-        module.dependencies.add(new org.gradle.plugins.idea.model.ModuleDependency('ui', null))
-    }
-}
-
-eclipseClasspath {
-    whenConfigured { classpath ->
-        classpath.entries.add(new org.gradle.plugins.eclipse.model.ProjectDependency('/ui', true, null, [] as Set))
-    }
-}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/GradleRunnerTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/GradleRunnerTest.groovy
deleted file mode 100644
index de3a16e..0000000
--- a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/GradleRunnerTest.groovy
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests
-
-import junit.framework.AssertionFailedError
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.openapi.external.runner.GradleRunnerFactory
-import org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1
-import org.gradle.openapi.external.runner.GradleRunnerVersion1
-import org.gradle.openapi.wrappers.RunnerWrapperFactory
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
- at RunWith(DistributionIntegrationTestRunner.class)
-class GradleRunnerTest {
-
-  static final String JAVA_PROJECT_NAME = 'javaproject'
-  static final String SHARED_NAME = 'shared'
-  static final String API_NAME = 'api'
-  static final String WEBAPP_NAME = 'webservice'
-  static final String SERVICES_NAME = 'services'
-  static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
-  private File javaprojectDir
-
-  @Rule public final GradleDistribution dist = new GradleDistribution()
-  @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-  @Rule public final Sample sample = new Sample('java/quickstart')
-
-  @Before
-  void setUp() {
-      javaprojectDir = sample.dir
-  }
-
-  /**
-   * We just want to make sure we can instantiate a GradleRunner here. That's all
-  */
-  @Test
-  public void testInstantiation()
-  {
-    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1(javaprojectDir)
-
-    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-  }
-
-  /**
-   * This does a basic execution. It also checks to make sure that the notifications were fired
-   * correctly.
-  */
-  @Test
-  public void testExecution()
-  {
-    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1( javaprojectDir )
-
-    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-
-    runner.executeCommand( "clean build" )
-
-        //wait for it to complete
-    int totalWaitTime = 0;
-    int maximumWaitTime = 80
-    while ( !interaction.executionFinished && totalWaitTime <= maximumWaitTime ) {
-        try {
-            Thread.sleep(1000);
-        }
-        catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        totalWaitTime += 1;
-    }
-
-    if( totalWaitTime > maximumWaitTime ) {
-      throw new AssertionFailedError( "Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + interaction.getWorkingDirectory() + "'\nOutput:\n" + interaction.output.toString() )
-    }
-
-    //now make sure we were notified of things correctly:
-
-    //it should have fired a message that execution has started
-    Assert.assertTrue( "Execution did not report started", interaction.executionStarted )
-    
-    //it should have finished
-    Assert.assertTrue( "Execution did not report finished", interaction.executionFinished )
-
-    //it should have been successful
-    Assert.assertTrue( "Did not execute command successfully", interaction.wasSuccessful )
-
-    //we should have output
-    Assert.assertTrue( "Missing output", interaction.output.length() > 0 )
-
-    //we should have a message when we finished (basically the full output)
-    Assert.assertTrue( "Missing finish message", interaction.finishMessage != null )
-
-    //there should have been multiple tasks to execute
-    Assert.assertTrue( "Not enough tasks executed. Expected multiple. Found " + interaction.numberOfTasksToExecute, interaction.numberOfTasksToExecute > 1 )
-
-    //we should have been notified that tasks started and completed (we're not interested in tracking how many times or specific tasks as that might change too often with releases of gradle.
-    Assert.assertTrue( "No tasks reported started", interaction.taskStarted )
-    Assert.assertTrue( "No tasks reported completed", interaction.taskCompleted )
-  }
-
-  /**
-   * This tests killing a task. We'll start a build task then kill it after it starts executing.
-   * Note: the kill interaction actually kills execution. It waits for a certain number of tasks
-   * to be executed.
-  */
-  @Test
-  public void testKill()
-  {
-    KillTestInteraction interaction = new KillTestInteraction(javaprojectDir)
-
-    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
-
-    interaction.runner = runner
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-
-    runner.executeCommand( "build" )
-
-        //wait for it to complete
-    int totalWaitTime = 0;
-    int maximumWaitTime = 80
-    while ( !interaction.executionFinished && totalWaitTime <= maximumWaitTime ) {
-        try {
-            Thread.sleep(1000);
-        }
-        catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        totalWaitTime += 1;
-    }
-
-    if( totalWaitTime > maximumWaitTime ) {
-      throw new AssertionFailedError( "Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + interaction.getWorkingDirectory() + "'\nOutput:\n" + interaction.output.toString() )
-    }
-
-    //make sure we tried to kill the task
-    Assert.assertTrue( "Did not attempt to kill execution", interaction.killedTask )
-
-    //now make sure we were notified of things correctly:
-
-    //it should NOT have been successful
-    Assert.assertFalse( "Erroneously executed successfully (was not killed)", interaction.wasSuccessful )
-
-    //it should have fired a message that execution has started
-    Assert.assertTrue( "Execution did not report started", interaction.executionStarted )
-
-    //it should have finished
-    Assert.assertTrue( "Execution did not report finished", interaction.executionFinished )
-  }
-   /**
-    * This directly verifies that our wrapper factory is working. We just want to use it to
-    * instantiate a runner.
-   */
-  @Test
-  public void testRunnerWrapperFactory()
-  {
-    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1( javaprojectDir )
-
-    GradleRunnerVersion1 runner = RunnerWrapperFactory.createGradleRunner( dist.getGradleHomeDir(), interaction, true)
-
-    Assert.assertNotNull( "Failed to instantiate runner", runner )
-  }
-}
-
-  //Inner class used to track what has been called
-  public class TestGradleRunnerInteractionVersion1 implements GradleRunnerInteractionVersion1
-  {
-    private File workingDirectory
-    private StringBuilder output = new StringBuilder()
-    private String finishMessage
-    boolean wasSuccessful
-    boolean executionStarted
-    int numberOfTasksToExecute
-    boolean executionFinished
-    boolean taskCompleted
-    boolean taskStarted
-
-
-    public TestGradleRunnerInteractionVersion1(File workingDirectory) {
-      this.workingDirectory = workingDirectory;
-    }
-
-    def TestGradleRunnerInteractionVersion1() {
-    }
-
-    File getWorkingDirectory() { return workingDirectory }
-
-    GradleRunnerInteractionVersion1.LogLevel getLogLevel() { return GradleRunnerInteractionVersion1.LogLevel.Lifecycle }
-
-    GradleRunnerInteractionVersion1.StackTraceLevel getStackTraceLevel() { return GradleRunnerInteractionVersion1.StackTraceLevel.InternalExceptions }
-
-    void reportExecutionStarted() { executionStarted = true }
-    void reportNumberOfTasksToExecute(int size) { numberOfTasksToExecute = size }
-
-    //both of these will be fired often. We're not going to try to track that.
-    void reportTaskStarted(String currentTaskName, float percentComplete) { taskStarted = true; }
-    void reportTaskComplete(String currentTaskName, float percentComplete) { taskCompleted = true }
-
-    void reportLiveOutput(String output) { this.output.append( output ) }
-
-    void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
-      this.executionFinished = true;
-      this.wasSuccessful = wasSuccessful
-      this.finishMessage = message;
-    }
-
-    File getCustomGradleExecutable() { return null; }
-  }
-
-
-  //class to track that has class was started and then kills it. 
-  private class KillTestInteraction implements GradleRunnerInteractionVersion1
-  {
-    private GradleRunnerVersion1 runner
-    int tasks = 0
-    boolean killedTask
-
-    //after at least 2 tasks start, try to kill the process. This simulates someone killing it while
-    //its in the middle of running
-    def void reportTaskStarted(String currentTaskName, float percentComplete) {
-      tasks++
-      if( tasks == 2 ) {
-        killedTask = true
-        runner.killProcess();
-      }
-    }
-    private File workingDirectory
-    boolean wasSuccessful
-    boolean executionStarted
-    boolean executionFinished
-
-    public KillTestInteraction(File workingDirectory) {
-      this.workingDirectory = workingDirectory;
-    }
-
-    File getWorkingDirectory() { return workingDirectory }
-
-    GradleRunnerInteractionVersion1.LogLevel getLogLevel() { return GradleRunnerInteractionVersion1.LogLevel.Lifecycle }
-
-    GradleRunnerInteractionVersion1.StackTraceLevel getStackTraceLevel() { return GradleRunnerInteractionVersion1.StackTraceLevel.InternalExceptions }
-
-    void reportExecutionStarted() { executionStarted = true }
-    void reportNumberOfTasksToExecute(int size) { }
-
-    void reportTaskComplete(String currentTaskName, float percentComplete) {}
-    void reportLiveOutput(String output) {}
-    File getCustomGradleExecutable() { return null; }
-
-    void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
-      this.executionFinished = true;
-      this.wasSuccessful = wasSuccessful
-    }
-  }
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/OpenApiUiTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/OpenApiUiTest.groovy
deleted file mode 100644
index 5728219..0000000
--- a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/OpenApiUiTest.groovy
+++ /dev/null
@@ -1,1288 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests
-
-import java.awt.BorderLayout
-import java.awt.Color
-import java.awt.Component
-import java.awt.event.HierarchyEvent
-import java.awt.event.HierarchyListener
-import javax.swing.JFrame
-import javax.swing.JLabel
-import javax.swing.JPanel
-import javax.swing.SwingUtilities
-import junit.framework.AssertionFailedError
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.openapi.external.ExternalUtility
-import org.gradle.openapi.external.foundation.favorites.FavoriteTaskVersion1
-import org.gradle.openapi.external.foundation.favorites.FavoritesEditorVersion1
-import org.gradle.openapi.wrappers.UIWrapperFactory
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.gradle.openapi.external.foundation.*
-import org.gradle.openapi.external.ui.*
-
-/**
- * This tests numerous aspects of the Open API UI. This is how the Idea plugin extracts the UI from
- * Gradle.
- */
- at RunWith(DistributionIntegrationTestRunner.class)
-public class OpenApiUiTest {
-  static final String JAVA_PROJECT_NAME = 'javaproject'
-  static final String SHARED_NAME = 'shared'
-  static final String API_NAME = 'api'
-  static final String WEBAPP_NAME = 'webservice'
-  static final String SERVICES_NAME = 'services'
-  static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
-  private File javaprojectDir
-
-  @Rule public final GradleDistribution dist = new GradleDistribution()
-  @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-  @Rule public final Sample sample = new Sample('java/quickstart')
-
-  @Before
-  void setUp() {
-      javaprojectDir = sample.dir
-  }
-
-     /**
-      This tests to see if we can call the UIFactory to create a single pane UI.
-      This is only testing that extracting the UI returns something without giving
-      errors and that it has a component. This is just a good general-case test
-      to make sure teh basics are working.
-      */
-    @Test
-    public void testSinglePaneBasic()
-    {
-        SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-
-        //make sure we got something
-        Assert.assertNotNull( singlePane )
-
-        //tell it we're about to show it, so it'll create a component
-        singlePane.aboutToShow();
-
-        //make sure we now have that component
-        Assert.assertNotNull( singlePane.getComponent() )
-    }
-
-  /**
-   * This is the same as testSinglePaneBasic but uses the UIWrapperFactory directly;
-   * which is what the UIFactory uses via reflection to instantiate the UI. Just
-   * making sure this is working normally and not throwing any exceptions.
-   */
-   @Test
-   public void testUIWrapperSinglePane()
-   {
-     TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1() )
-     SinglePaneUIVersion1 singlePane = UIWrapperFactory.createSinglePaneUI(testSingleDualPaneUIInteractionVersion1, false)
-
-     //tell it we're about to show it, so it'll create a component
-     singlePane.aboutToShow();
-
-     //make sure we now have that component
-     Assert.assertNotNull( singlePane.getComponent() )
-   }
-
-  /**
-   * This tests to see if we can call the UIFactory to create a dual pane UI.
-      This is only testing that extracting the UI returns something without giving
-      errors and that it has its dual components. This is just a good general-case test
-      to make sure teh basics are working.
-   */
-    @Test
-    public void testDualPaneBasic()
-    {
-        DualPaneUIVersion1 dualPane = createDualPaneUI()
-
-        //make sure we got something
-        Assert.assertNotNull( dualPane )
-
-        //tell it we're about to show it, so it'll create a component
-        dualPane.aboutToShow();
-
-        //make sure we now have the main component
-        Assert.assertNotNull( dualPane.getMainComponent() )
-
-        //and the output component
-        Assert.assertNotNull( dualPane.getOutputPanel() )
-    }
-
-  /**
-   * This is the same as testDualPaneBasic but uses the UIWrapperFactory directly;
-   * which is what the UIFactory uses via reflection to instantiate the UI. Just
-   * making sure this is working normally and not throwing any exceptions.
-   */
-   @Test
-   public void testUIWrapperDualPane()
-   {
-     TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1() )
-     DualPaneUIVersion1 dualPane = UIWrapperFactory.createDualPaneUI(testSingleDualPaneUIInteractionVersion1, false)
-
-     //tell it we're about to show it, so it'll create a component
-     dualPane.aboutToShow();
-
-     //make sure we now have that component
-     Assert.assertNotNull( dualPane.getMainComponent() )
-     Assert.assertNotNull( dualPane.getOutputPanel() )
-   }
-
-   /**
-   * Helper function that creates a single pane UI
-   */
-   private SinglePaneUIVersion1 createSinglePaneUI()
-   {
-     TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1() )
-     SinglePaneUIVersion1 singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false )
-
-     //make sure we got something
-     Assert.assertNotNull( singlePane )
-
-     singlePane.setCurrentDirectory( javaprojectDir )
-
-     return singlePane
-   }
-
-  /**
-   * Helper function that creates a dual pane UI
-   */
-   private DualPaneUIVersion1 createDualPaneUI()
-   {
-     TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1() )
-     DualPaneUIVersion1 dualPane = UIFactory.createDualPaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false )
-
-     //make sure we got something
-     Assert.assertNotNull( dualPane )
-
-     dualPane.setCurrentDirectory( javaprojectDir )
-
-     return dualPane
-   }
-
-    /**
-    * This verifies that favorites are working for some basics. We're going to test this with both
-     * the single and dual pane UIs (they actually use the same editor then for other tests we'll
-     * assume they're same).
-    */
-    @Test
-    public void testFavoritesBasic()
-    {
-      SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-      checkFavoritesBasic( singlePane )
-
-      DualPaneUIVersion1 dualPane = createDualPaneUI()
-      checkFavoritesBasic( dualPane )
-    }
-
-    /**
-    * This verifies that we favorites are basically working based on the given UI. We're going to add one, then
-    * do some 'gets' to find the just-added favorite.
-    */
-    private void checkFavoritesBasic( BasicGradleUIVersion1 basicGradleUI )
-    {
-      FavoritesEditorVersion1 editor = basicGradleUI.getFavoritesEditor()
-
-      //there should be no favorites as of yet
-      Assert.assertTrue( editor.getFavoriteTasks().isEmpty() )
-
-      //add one (doesn't really matter what it is)
-      def fullCommandLine = "-t -S"
-      def displayName = "Task List With Stack trace"
-      FavoriteTaskVersion1 favorite = editor.addFavorite( fullCommandLine, displayName, true )
-
-      //make sure something was added
-      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
-
-      //get the newly-added favorite by command line.
-      FavoriteTaskVersion1 matchingFavorite1 = editor.getFavorite( fullCommandLine )
-      Assert.assertEquals( favorite, matchingFavorite1 )
-
-      //get the newly-added favorite by displayName.
-      FavoriteTaskVersion1 matchingFavorite2 = editor.getFavoriteByDisplayName( displayName )
-      Assert.assertEquals( favorite, matchingFavorite2 )
-
-      Assert.assertTrue( matchingFavorite2.alwaysShowOutput() )
-    }
-
-    /**
-    * This verifies that we can edit favorites. We're going to add a favorite then edit its
-     * command line, display name, and 'show output' setting.
-    */
-    @Test
-    public void testEditingFavorites()
-    {
-      SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-      FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
-
-      def originalFullCommandLine = "-t -S"
-      def originalDisplayName = "Task List With Stack trace"
-      FavoriteTaskVersion1 addedFavorite = editor.addFavorite(originalFullCommandLine, originalDisplayName, true)
-
-      //make sure we can find the just-added favorite
-      Assert.assertNotNull( editor.getFavorite( originalFullCommandLine ) )
-      Assert.assertNotNull( editor.getFavoriteByDisplayName( originalDisplayName ) )
-
-      String newFullCommandLine = "-t -S -d"
-      String newDisplayName = "new task list"
-      String error = editor.editFavorite( addedFavorite, newFullCommandLine, newDisplayName, false )
-      Assert.assertNull( error )  //we should get no error
-
-      //now we shouldn't be able to find the favorite using the original values. This is part of verifying the values were in fact changed.
-      Assert.assertNull( editor.getFavorite( originalFullCommandLine ) )
-      Assert.assertNull( editor.getFavoriteByDisplayName( originalDisplayName ) )
-
-      //make sure we can find it using the new values. This is part of verifying the values were in fact changed.
-      Assert.assertNotNull( editor.getFavorite( newFullCommandLine ) )
-      Assert.assertNotNull( editor.getFavoriteByDisplayName( newDisplayName ) )
-
-      //there should just be 1 favorite
-      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
-    }
-
-    /**
-    * This verifies that we can remove favorites. We're going to add some favorites then remove them
-     * verifying that they've gone.
-    */
-    @Test
-    public void testRemovingFavorites()
-    {
-      SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-      FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
-
-      //there should be no favorites as of yet
-      Assert.assertTrue( editor.getFavoriteTasks().isEmpty() )
-
-      //add one (doesn't really matter what it is)
-      String command1 = "build"
-      FavoriteTaskVersion1 favorite1 = editor.addFavorite( command1, command1, true )
-
-      //make sure it was added
-      Assert.assertNotNull( editor.getFavorite( command1 ) )
-      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
-
-      //add another one
-      String command2 = "build -xtest"
-      FavoriteTaskVersion1 favorite2 = editor.addFavorite( command2, command2, true )
-
-      //make sure it was added
-      Assert.assertNotNull( editor.getFavorite( command2 ) )
-      Assert.assertEquals( 2, editor.getFavoriteTasks().size() )
-
-      String command3 = "clean"
-      FavoriteTaskVersion1 favorite3 = editor.addFavorite( command3, command3, true )
-
-      //make sure it was added
-      Assert.assertNotNull( editor.getFavorite( command3 ) )
-      Assert.assertEquals( 3, editor.getFavoriteTasks().size() )
-
-      String command4 = "docs"
-      FavoriteTaskVersion1 favorite4 = editor.addFavorite( command4, command4, true )
-
-      //make sure it was added
-      Assert.assertNotNull( editor.getFavorite( command4 ) )
-      Assert.assertEquals( 4, editor.getFavoriteTasks().size() )
-
-      //now remove one of them
-      List removed1 = [ favorite2 ]
-      editor.removeFavorites( removed1 )
-
-      //make sure it was removed
-      Assert.assertNull( editor.getFavorite( command2 ) )
-      Assert.assertEquals( 3, editor.getFavoriteTasks().size() )
-
-      //now remove multiples
-      List removed2 = [ favorite1, favorite4 ]
-      editor.removeFavorites( removed2 )
-
-      //make sure they were both removed
-      Assert.assertNull( editor.getFavorite( command1 ) )
-      Assert.assertNull( editor.getFavorite( command4 ) )
-      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
-    }
-
-    /**
-   * This tests executing multiple favorites at once. We'll add two favorites, then execute both of them
-   * via GradleInterfaceVersion1.executeFavorites(). This should execute both as a single command
-   * concatenating them together.
-   */
-    @Test
-    public void testExecutingFavorites()
-    {
-      SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-      FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
-
-      //this starts the execution queue
-      singlePane.aboutToShow()
-
-      //there should be no favorites as of yet
-      Assert.assertTrue( editor.getFavoriteTasks().isEmpty() )
-
-      //add one (doesn't really matter what it is)
-      String command1 = "build"
-      FavoriteTaskVersion1 favorite1 = editor.addFavorite( command1, command1, true )
-
-      //make sure it was added
-      Assert.assertNotNull( editor.getFavorite( command1 ) )
-      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
-
-      //add another one
-      String command2 = "clean"
-      FavoriteTaskVersion1 favorite2 = editor.addFavorite( command2, command2, true )
-
-      //make sure it was added
-      Assert.assertNotNull( editor.getFavorite( command2 ) )
-      Assert.assertEquals( 2, editor.getFavoriteTasks().size() )
-
-      //add a request observer so we can observe when the command is finished. This allows us to
-      //see what was actually executed.
-      TestRequestObserver testRequestObserver = new TestRequestObserver( RequestVersion1.EXECUTION_TYPE )
-      ((GradleInterfaceVersion2)singlePane.getGradleInterfaceVersion1()).addRequestObserver( testRequestObserver )
-
-      //now execute both favorites
-      List<FavoriteTaskVersion1> favorites = [ favorite1, favorite2 ]
-      RequestVersion1 request = ( (GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1() ).executeFavorites(favorites)
-
-      Assert.assertNotNull( request )
-
-      //verify that the actual command that was executed is a concatenation of both favorites
-      Assert.assertEquals( "build clean", request.getFullCommandLine() )
-    }
-
-    /**
-    * This tests getting projects and tasks from gradle. It then goes through a series of tests
-    * related to projects and tasks (such as making sure their description is returned, their
-    * parent is returned, etc).
-    */
-    @Test
-    public void testProjectsAndTasks()
-    {
-      DualPaneUIVersion1 dualPane = createDualPaneUI()
-      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-      //make sure our samples directory exists
-      if( !gradleInterface.getCurrentDirectory().exists() ) {
-        throw new AssertionFailedError('sample project missing. Expected it at: ' + gradleInterface.getCurrentDirectory())
-      }
-
-      //add a request observer so we can observe when the command is finished. This allows us to
-      //see what was actually executed.
-      TestRequestObserver testRequestObserver = new TestRequestObserver( RequestVersion1.REFRESH_TYPE )
-      gradleInterface.addRequestObserver( testRequestObserver )
-
-      //this starts the execution queue
-      dualPane.aboutToShow()
-
-      gradleInterface.refreshTaskTree()
-
-      //now we'll wait up to x seconds (arbitrary) for the refresh to occur. This is ugly, but its just a test.
-      int maximumWaitTime = 40;
-      int totalWaitTime = 0;
-      while ( testRequestObserver.request == null && totalWaitTime <= maximumWaitTime ) {
-          try {
-              Thread.sleep(1000);
-          }
-          catch (InterruptedException e) {
-              e.printStackTrace();
-          }
-
-          totalWaitTime += 1;
-      }
-
-      if( totalWaitTime > maximumWaitTime ) {
-        throw new AssertionFailedError("Waited " + totalWaitTime + " seconds and failed to get root projects. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + gradleInterface.getCurrentDirectory() + "'\ngradle home: '" + gradleInterface.getGradleHomeDirectory() + "'")
-      }
-
-      Assert.assertEquals( "Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
-
-      List<ProjectVersion1> rootProjects = gradleInterface.getRootProjects();
-      Assert.assertFalse( rootProjects.isEmpty() );   //do we have any root projects?
-
-      ProjectVersion1 rootProject = rootProjects.get( 0 );
-      Assert.assertNotNull( rootProject );
-
-      //Quick check to make sure there are tasks on each of the sub projects.
-      //The exact task names will change over time, so I don't want to try
-      //to test for those. I'll just make sure there are several.
-      Iterator<ProjectVersion1> iterator = rootProjects.get(0).getSubProjects().iterator();
-      while( iterator.hasNext() )
-      {
-         ProjectVersion1 projectVersion1 = iterator.next();
-         Assert.assertTrue( projectVersion1.getTasks().size() > 4 );
-      }
-
-      //there should be a 'services' project
-      ProjectVersion1 servicesProject = rootProjects.get(0).getSubProject("services" );
-      Assert.assertNotNull( servicesProject );
-
-      //and it contains a 'webservice' sub project
-      ProjectVersion1 webserviceProject = servicesProject.getSubProject("webservice");
-      Assert.assertNotNull( webserviceProject );
-
-      ProjectVersion1 apiProject = rootProjects.get(0).getSubProject("api");
-      Assert.assertNotNull( apiProject );
-
-      //verify the parent project is set correctly
-      Assert.assertEquals( servicesProject, webserviceProject.getParentProject() )
-
-      //verify its full name is correct (this might should be prefixed with a colon)
-      Assert.assertEquals( "services:webservice", webserviceProject.getFullProjectName() )
-
-      //verify getSubProjectFromFullPath works
-      ProjectVersion1 foundProject = rootProject.getSubProjectFromFullPath("services:webservice")
-      Assert.assertNotNull( "Failed to find services:webservice", foundProject )
-      Assert.assertEquals( webserviceProject, foundProject )
-
-      //verify that are multiple tasks here (we know their should be)
-      Assert.assertTrue( webserviceProject.getTasks().size() > 4 );
-
-      //verify getTaskFromFullPath works
-      TaskVersion1 apiBuildTask = rootProject.getTaskFromFullPath(":api:build")
-      Assert.assertNotNull( "Failed to find :api:build", apiBuildTask )
-
-      Assert.assertEquals( apiProject, apiBuildTask.getProject() )
-
-      //then make sure it has a description
-      Assert.assertNotNull( apiBuildTask.getDescription() )
-
-      //and that its not marked as the default (we need a task to be the default so we can verify it returns true)
-      Assert.assertFalse( apiBuildTask.isDefault() )
-
-      //there are no default tasks here
-      Assert.assertTrue( apiProject.getDefaultTasks().isEmpty() )
-
-      //this build task is a child of the api project. Should be the same task we got earlier
-      TaskVersion1 buildTask = apiProject.getTask("build")
-      Assert.assertNotNull( "Failed to find build task", buildTask )
-      Assert.assertEquals( apiBuildTask, buildTask )
-    }
-
-   /**
-    * This verifies that the GradleInterfaceVersion1.refreshTaskTree that takes
-    * additional arguments works. We're not really interested in what those additional
-    * arguments are, just that it passes them along.
-    */
-    @Test
-    public void testRefreshWithArguments()
-    {
-      DualPaneUIVersion1 dualPane = createDualPaneUI()
-      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-      //make sure our samples directory exists
-      if( !gradleInterface.getCurrentDirectory().exists() ) {
-        throw new AssertionFailedError('sample project missing. Expected it at: ' + gradleInterface.getCurrentDirectory())
-      }
-
-      //this starts the execution queue
-      dualPane.aboutToShow()
-
-      //add a request observer so we can observe when the command is finished. This allows us to
-      //see what was actually executed.
-      TestRequestObserver testRequestObserver = new TestRequestObserver( RequestVersion1.REFRESH_TYPE )
-      gradleInterface.addRequestObserver( testRequestObserver )
-
-      RequestVersion1 request = gradleInterface.refreshTaskTree2("-xtest")
-
-      //make sure that the actual request is the normal refresh request with our
-      //(this line is really what we're trying to test)
-      Assert.assertEquals( "-t -xtest", request.getFullCommandLine() )
-
-      //now we'll wait up to x seconds (arbitrary) for the refresh to occur. This is ugly, but its just a test.
-      int maximumWaitTime = 20;
-      int totalWaitTime = 0;
-      while ( testRequestObserver.request == null && totalWaitTime <= maximumWaitTime ) {
-          try {
-              Thread.sleep(1000);
-          }
-          catch (InterruptedException e) {
-              e.printStackTrace();
-          }
-
-          totalWaitTime += 1;
-      }
-
-      if( totalWaitTime > maximumWaitTime ) {
-        throw new AssertionFailedError("Waited " + totalWaitTime + " seconds and failed to complete refresh. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + gradleInterface.getCurrentDirectory() + "'\ngradle home: '" + gradleInterface.getGradleHomeDirectory() + "'")
-      }
-
-      Assert.assertEquals( "Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
-
-      Assert.assertEquals( "Not our request", request, testRequestObserver.request );
-    }
-
-    /**
-    * This verifies that you can add custom stuff to the setup tab. This is a UI test and is kinda tricky. We're going
-    * to use a HierarchyListener to see if our component is made visible. This will confirm if it was added or not
-    * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
-    * really doing here, is adding a 'custom' component to the UI, then adding the UI to a frame, then showing the frame,
-    * so we can verify that our component was shown.
-    */
-    @Test
-    public void testAddingComponentToSetupTab()
-    {
-      if ( java.awt.GraphicsEnvironment.isHeadless() ) {
-        return;  // Can't run this test in headless mode!
-       }
-
-      JLabel label = new JLabel("Testing Testing 123")
-      TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
-      label.addHierarchyListener( hierarchyAdapter )
-
-      SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-
-      //make sure we haven't been told the component was shown or hidden yet
-      Assert.assertFalse( hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( hierarchyAdapter.componentWasHidden )
-
-      singlePane.aboutToShow();
-
-      singlePane.setCustomPanelToSetupTab( label )
-
-      //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
-      Assert.assertFalse( hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( hierarchyAdapter.componentWasHidden )
-
-      //now create a frame, place the UI in it, then show it briefly
-      JFrame frame = createTestFrame( singlePane.getComponent(), null ) //null because the single pane is entirely self contained. There is no output panel.
-
-      //set the Setup tab as the current tab. This is required to actually show the component.
-      int setupTabIndex = singlePane.getGradleTabIndex( "Setup" );
-      Assert.assertTrue( "Failed to get index of setup tab", setupTabIndex != -1 )
-      singlePane.setCurrentGradleTab( setupTabIndex );
-
-      //still should not show the component (its not yet visible, but is about to be)
-      Assert.assertFalse( hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( hierarchyAdapter.componentWasHidden )
-
-
-      //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
-      //queue. This is required for the setup tab to become current as well as show the custom component we added.
-      showFrameEmptyEventQueueHide( frame )
-
-      Assert.assertEquals( "The setup tab was not selected", setupTabIndex, singlePane.getCurrentGradleTab() )
-
-      //now the label should have been made visible then invisible
-      Assert.assertTrue( hierarchyAdapter.componentWasShown )
-      Assert.assertTrue( hierarchyAdapter.componentWasHidden )
-    }
-
-    /**
-    * This verifies that you can add a custom tab to the UI. This is a UI test and is kinda tricky. We're going
-    * to use a HierarchyListener to see if our tab component is made visible. This will confirm if it was added or not
-    * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
-    * really doing here, is adding a tab to the UI, then adding the UI to a frame, then showing the frame so we can
-    * then verify that our tab was shown (actually using our tab's component).
-    */
-    @Test
-    public void testAddingCustomTab()
-    {
-      if ( java.awt.GraphicsEnvironment.isHeadless() ) {
-        return;  // Can't run this test in headless mode!
-      }
-
-      TestTab testTab = new TestTab()
-
-      SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-
-      //make sure we haven't been told the component was shown or hidden yet
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
-
-      //make sure things are initialized properly. These should all be false
-      Assert.assertFalse( testTab.nameRetrieved );
-      Assert.assertFalse( testTab.informedAboutToShow );
-      Assert.assertFalse( testTab.componentCreated );
-
-      int originalCount = singlePane.getGradleTabCount();
-
-      singlePane.addTab( 99, testTab ) //I don't really care about the index. It should accept a number that is too large and handle it appropriately.
-
-      singlePane.aboutToShow()
-
-      //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
-
-      //now create a frame, place the UI in it, then show it briefly
-      JFrame frame = createTestFrame( singlePane.getComponent(), null ) //null because the single pane is entirely self contained. There is no output panel.
-
-      String testTabName = "Test Tab"
-
-      //set the test tab as the current tab. This is required to actually show the component.
-      int testTabIndex = singlePane.getGradleTabIndex( testTabName )
-      Assert.assertTrue( "Failed to get index of test tab", testTabIndex != -1 )
-      singlePane.setCurrentGradleTab( testTabIndex )
-
-      //just to test getGradleTabName, make sure it returns our tab name
-      Assert.assertEquals( testTabName, singlePane.getGradleTabName( testTabIndex ) )
-
-      //to test getGradleTabCount, make sure the tab count went up by 1
-      Assert.assertEquals( originalCount + 1, singlePane.getGradleTabCount() )
-
-      //still should not show the component (its not yet visible, but is about to be)
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
-
-      //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
-      //queue. This is required for the test tab to become current.
-      showFrameEmptyEventQueueHide( frame )
-
-      Assert.assertEquals( "The test tab was not selected", testTabIndex, singlePane.getCurrentGradleTab() )
-
-      //now the label should have been made visible then invisible
-      Assert.assertTrue( testTab.hierarchyAdapter.componentWasShown )
-      Assert.assertTrue( testTab.hierarchyAdapter.componentWasHidden )
-
-      //at the end, the name should have been queried, we should have been told we were about to shown, and the component should be created
-      Assert.assertTrue( testTab.nameRetrieved );
-      Assert.assertTrue( testTab.informedAboutToShow );
-      Assert.assertTrue( testTab.componentCreated );
-
-      //reset the test tab (resets the listener so we can remove the tab and verify that it no longer shows up, as well as some of our test variables)
-      testTab.reset()
-      singlePane.removeTab( testTab )
-
-      //I'm going to set the current tab, but this shouldn't do anything because the tab was removed
-      singlePane.setCurrentGradleTab( testTabIndex );
-
-      //part of showing the UI is telling it its about to be shown. In this case, nothing should happen
-      //related to the test tab. It has been removed
-      singlePane.aboutToShow()
-
-      //This shows and hides the UI, giving it time to actually show itself and empty the event
-      //dispatch queue. This is required for the test tab to become current (were it still present).
-      showFrameEmptyEventQueueHide( frame )
-
-      //try to get the test tab
-      testTabIndex = singlePane.getGradleTabIndex( "Test Tab" );
-      Assert.assertTrue( "Erroneously got index of test tab. It was removed", testTabIndex == -1 )
-
-      //we've removed it, so it shouldn't have been polled about or informed of anything
-      Assert.assertFalse( testTab.nameRetrieved );
-      Assert.assertFalse( testTab.informedAboutToShow );
-      Assert.assertFalse( testTab.componentCreated );
-
-      //It was not shown after the reset, these should both be false
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
-      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
-    }
-
-  /**
-   * This creates a frame with the specified main component (presumably the Gradle UI).
-   * This also shows large text explaining what this so developers seeing this won't
-   * freak out too much when running tests.
-   * @param mainComponent the component that goes in the center of our frame
-   * @param outputComponent an optional 'output pane' component that goes along the bottom  
-   */
-    private JFrame createTestFrame( Component mainComponent, Component outputComponent )
-    {
-      JFrame frame = new JFrame()
-      frame.setSize( 650, 500 )
-      JPanel panel = new JPanel( new BorderLayout() )
-      frame.getContentPane().add( panel )
-
-      //add a large red label explaining this
-      JLabel label2 = new JLabel("Performing Open API Integration Test!")
-      label2.setForeground( Color.red )
-      label2.setFont( label2.getFont().deriveFont( 26f ) );
-      panel.add( label2, BorderLayout.NORTH )
-
-      //add the main UI to the frame
-      panel.add( mainComponent, BorderLayout.CENTER )
-
-      if( outputComponent != null ) {
-        panel.add( outputComponent, BorderLayout.SOUTH )
-      }
-
-      return frame;
-    }
-
-    /*
-     * This shows the specified frame for a moment so the event queue can be emptied. This
-     * ensures Swing events a processed
-     * Don't place anything between the following three lines (especially something that might
-     * throw an exception). This shows and hides the UI, giving it time to actually show itself
-     * and empty the event dispatch queue.
-     */
-    private void showFrameEmptyEventQueueHide( JFrame frame )
-    {
-      SwingUtilities.invokeAndWait( {frame.setVisible( true ) } )
-      Thread.currentThread().sleep( 500 );
-      SwingUtilities.invokeAndWait( {frame.setVisible( false ) } )
-    }
-
-    /**
-    * We want to make sure the settings are working correctly here. This is the mechanism that
-     * handles saving/restoring the values within the UI and can be stored in different ways
-     * depending on how the UI integrated with its parent (its up to whoever implements
-     * SettingsNodeVersion1). Here, to spot check that the basics are working, we'll create a
-     * UI, set a value, close it, then recreate it using the same settings object. The values
-     * should be saved upon close and then restored.
-    */
-    @Test
-    public void testSettings()
-    {
-      TestSettingsNodeVersion1 settingsNode = new TestSettingsNodeVersion1();
-
-      TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), settingsNode );
-        SinglePaneUIVersion1 singlePane = null;
-        try {
-            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false );
-        } catch (Exception e) {
-            throw new AssertionFailedError( "Failed to extract single pane: Caused by " + e.getMessage() )
-        }
-
-        File illegalDirectory = createTempDirectory( "non-existant" );
-        try {
-            if( illegalDirectory.equals( singlePane.getCurrentDirectory() ) ) {
-              throw new AssertionFailedError( "Directory already set to 'test' directory. The test is not setup correctly." );
-            }
-
-            //this is required to get the ball rolling
-            singlePane.aboutToShow();
-
-            //set the current directory after calling aboutToShow (otherwise, it'll stomp over us when it restores its default settings)
-            singlePane.setCurrentDirectory( illegalDirectory );
-
-            //close the UI. This saves the current settings.
-            singlePane.close();
-
-            //now instantiate it again
-            testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), settingsNode );
-            try {
-                singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false );
-            } catch (Exception e) {
-                throw new AssertionFailedError( "Failed to extract single pane (second time): Caused by " + e.getMessage() )
-            }
-
-            //this should restore the previous settings
-            singlePane.aboutToShow();
-
-            Assert.assertEquals( illegalDirectory, singlePane.getCurrentDirectory() );
-        } finally {
-            deleteTempDirectory( illegalDirectory );
-        }
-    }
-
-    /**
-      Call this to create a temporary directory with the specified name.
-
-      <!      Name       Description>
-      @param  baseName   the base name. We may append a number to it.
-      @return the directory.
-      @author mhunsicker
-   */
-   public static File createTempDirectory( String baseName )
-   {
-      String systemTemporaryDirectory = System.getProperty( "java.io.tmpdir" );
-
-      File file = new File( systemTemporaryDirectory, baseName );
-      int index = 2;
-      while( file.exists() )
-      {
-         file = new File( systemTemporaryDirectory, baseName + "_" + index );
-         index++;
-      }
-
-      file.mkdirs(); //create it
-      return file;
-   }
-
-   /**
-      This deletes the contents of a temporary directory (created with the above
-      function) and hopefully the directory itself. BUt this doesn't work very
-      well on Windows. Sun says it's officially a Windows issue. I tend to agree
-      since Windows mysteriously locks files and won't let you delete them and
-      only Explorer has a handle to them (verified using process explorer).
-
-      @author mhunsicker
-   */
-   public static void deleteTempDirectory( File temporaryDirectory )
-   {
-      File[] files = temporaryDirectory.listFiles();
-      if( files != null ) {
-        for (int index = 0; index < files.length; index++) {
-          File file = files[index];
-          file.delete();
-        }
-      }
-
-
-     temporaryDirectory.deleteOnExit();
-   }
-
-    /**
-     * This tests that the command line altering mechanism works. This adds additional
-     * things to the command line being executed. This is used by gradle build systems
-     * that need custom, system-specific arguments passed to it that aren't known by gradle
-     * proper. For example: you're working with a large project made of MANY subprojects, a
-     * custom IDE plugin can track which projects you're focusing on and pass that information
-     * to the build system via this mechanism, so it only builds appropriate projects.
-     * This is awkward to test because its real use requires a customized build system,
-     * so I'm going to pass an argument that is illegal by itself -- meaning no tasks
-     * are specified. Then I'll alter the command line by adding an actual task. Then
-     * wait for it to complete and verify what was executed
-    */
-    @Test
-    public void testCommandLineAlteringListener()
-    {
-      DualPaneUIVersion1 dualPane = createDualPaneUI()
-      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-      //this starts the execution queue. This also initiates a refresh that we'll ignore later.
-      dualPane.aboutToShow()
-
-      //add a request observer so we can observe when the command is finished. This allows us to
-      //see what was actually executed.
-      TestRequestObserver testRequestObserver = new TestRequestObserver( RequestVersion1.EXECUTION_TYPE )
-      gradleInterface.addRequestObserver( testRequestObserver )
-
-      //now that we know that command is illegal by itself, try it again but the listener will append 'build'
-      //to the command line which makes it legal (again, we don't really care what we execute.
-      TestCommandLineArgumentAlteringListenerVersion1 commandLineArgumentAlteringListener = new TestCommandLineArgumentAlteringListenerVersion1("build")
-      gradleInterface.addCommandLineArgumentAlteringListener( commandLineArgumentAlteringListener )
-
-      //execute this before we do our test. This is not legal by itself. It should fail. That means our
-      //test is setup correctly. For example: if someone adds a default task to this project, this will
-      //generate NO error and thus, our test will prove nothing. If you get a test failure here, you
-      //can try changing the command line to something that's illegal by itself (we don't care what).
-      RequestVersion1 request = gradleInterface.executeCommand2("-s", "test command")
-
-      //now we'll wait up to x seconds (arbitrary) for the task to execute. This is ugly, but its just a test.
-      int maximumWaitTime = 80;
-      int totalWaitTime = 0;
-      while ( testRequestObserver.request == null && totalWaitTime <= maximumWaitTime ) {
-          try {
-              Thread.sleep(1000);
-          }
-          catch (InterruptedException e) {
-              e.printStackTrace();
-          }
-
-          totalWaitTime += 1;
-      }
-
-      if( totalWaitTime > maximumWaitTime ) {
-        throw new AssertionFailedError("Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + gradleInterface.getCurrentDirectory() + "'\ngradle home: '" + gradleInterface.getGradleHomeDirectory() + "'\nOutput:\n" + testRequestObserver.output)
-      }
-
-      Assert.assertEquals( "Incorrect request", "-s build", testRequestObserver.request.getFullCommandLine() )
-
-      //make sure it completed execution correctly
-      Assert.assertEquals( "Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output , 0, testRequestObserver.result )
-
-      //the request that was executed should be equal to our original command with our 'altered' command added to it
-      Assert.assertNotNull( "Missing 'execution completed' request", testRequestObserver.request )
-
-      //just to be paranoid, let's make sure it was actually our request. If this fails, it probably represents something
-      //fundamentally flawed with the request or request wrapper mechanism.
-      Assert.assertEquals( request, testRequestObserver.request )
-
-      gradleInterface.removeRequestObserver( testRequestObserver )
-      gradleInterface.removeCommandLineArgumentAlteringListener( commandLineArgumentAlteringListener )
-    }
-
-   /**
-    * This tests that getVersion returns the same thing as the jar's suffix.
-    * We'll get the gradle jar, then strip off its extension and verify that
-    * the jar's name ends with the version number.
-    */
-  @Test
-  public void testVersion()
-  {
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-    String version = ( (GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).getVersion()
-
-    Assert.assertNotNull( "null version number", version )
-
-    Assert.assertFalse( "Empty version number", version.trim().equals( "" ) )       //shouldn't be empty
-
-    File gradleJar = ExternalUtility.getGradleJar(dist.gradleHomeDir)
-
-    Assert.assertNotNull( "Missing gradle jar", gradleJar )                         //we should have a gradle jar
-
-    int indexOfExtension = gradleJar.getName().toLowerCase().lastIndexOf( ".jar" )  //get the index of its extension
-
-    Assert.assertTrue( "Has no '.jar' extension", indexOfExtension != -1 )          //it had better have an extension
-
-    String name = gradleJar.getName().substring( 0, indexOfExtension )              //get its name minus the extension
-
-    Assert.assertTrue( "Jar name doesn't end with version", name.endsWith( version ) )  //the name (minus extension) should end with the version
-  }
-
-  /**
-   * This is just a spot check that getGradleHomeDirectory works. Its based off
-   * of the gradle you're running.
-   */
-  @Test
-  public void testGradleHomeDirectory()
-  {
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-
-    Assert.assertEquals( dist.gradleHomeDir, singlePane.getGradleHomeDirectory() )
-  }
-
-
-  /**
-   * This is just a spot check that we can get an instance of the OutputUILord.
-   * Other tests cover its functionality more thoroughly. This is just to make sure
-   * its working when accessed via the Open API.
-   */
-  @Test
-  public void testOutputUILord()
-  {
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-    Assert.assertNotNull( outputUILord )
-  }
-
-  /**
-   * This tests that you can correctly obtain the number of output tabs from a
-   * dual pane UI. This
-   */
-  @Test
-  public void testDualPaneOutputPaneNumber()
-  {
-    if ( java.awt.GraphicsEnvironment.isHeadless() ) {
-      return;  // Can't run this test in headless mode!
-    }
-
-    DualPaneUIVersion1 dualPane = createDualPaneUI()
-
-    //now create a frame, place the UI in it, then show it briefly
-    JFrame frame = createTestFrame( dualPane.getMainComponent(), dualPane.getOutputPanel() )
-
-    //make sure we got something
-    Assert.assertNotNull( dualPane )
-
-    //tell it we're about to show it, so it'll create a component
-    dualPane.aboutToShow()
-
-    dualPane.refreshTaskTree()
-
-    showFrameEmptyEventQueueHide( frame )
-
-    //there should be one opened output tab for the refresh
-    Assert.assertEquals( 1, dualPane.getNumberOfOpenedOutputTabs() )
-
-    dualPane.executeCommand( "build", "build" )
-
-    showFrameEmptyEventQueueHide( frame )
-
-    //there should be 2 opened output tabs. One for refresh, one for build
-    Assert.assertEquals( 2, dualPane.getNumberOfOpenedOutputTabs() )
-  }
-
-   /**
-  * This tests whether or not a the UI is considered busy. Its busy if its
-    * executing a command. To test this, we'll execute a command and verify
-    * we're busy. When it finishes, we'll verify we're not longer busy.
-    * We'll also check that canClose works properly. If we're busy, calling
-    * canClose should prompt the user to confirm closing and return their
-    * answer. If we're not busy, it should not prompt the user and return
-    * true.
-   */
-  @Test
-  public void testBusy()
-  {
-      DualPaneUIVersion1 dualPane = createDualPaneUI()
-      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-      //this starts the execution queue. This also initiates a refresh that we'll ignore later.
-      dualPane.aboutToShow()
-
-      //add a request observer so we can observe when the command is finished.
-      TestRequestObserver testRequestObserver = new TestRequestObserver( RequestVersion1.EXECUTION_TYPE )
-      gradleInterface.addRequestObserver( testRequestObserver )
-
-      gradleInterface.executeCommand("build", "test command")
-
-      //now that there's a real command in the queue, we should be considered busy
-      Assert.assertTrue( dualPane.isBusy() )
-      Assert.assertTrue( gradleInterface.isBusy() )
-
-      //we're busy, we shouldn't be able to close
-      TestCloseInteraction testCloseInteraction = new TestCloseInteraction( false )
-      Assert.assertFalse( dualPane.canClose( testCloseInteraction ) )
-
-      //since we just asked to close and we're busy, make sure we prompted the user
-      Assert.assertTrue( testCloseInteraction.wasPromptedToConfirmClose )
-
-      //now we'll wait up to x seconds (arbitrary) for the task to execute. This is ugly, but its just a test.
-      int maximumWaitTime = 80;
-      int totalWaitTime = 0;
-      while ( testRequestObserver.request == null && totalWaitTime <= maximumWaitTime ) {
-          try {
-              Thread.sleep(1000);
-          }
-          catch (InterruptedException e) {
-              e.printStackTrace();
-          }
-
-          totalWaitTime += 1;
-      }
-
-      if( totalWaitTime > maximumWaitTime ) {
-        throw new AssertionFailedError("Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + gradleInterface.getCurrentDirectory() + "'\ngradle home: '" + gradleInterface.getGradleHomeDirectory() + "'\nOutput:\n" + testRequestObserver.output)
-      }
-
-      Assert.assertEquals( "Incorrect request", "build", testRequestObserver.request.getFullCommandLine() )
-
-      //make sure it completed execution correctly
-      Assert.assertEquals( "Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output , 0, testRequestObserver.result )
-
-      //make sure we're not longer considered busy
-      Assert.assertFalse( dualPane.isBusy() )
-      Assert.assertFalse( gradleInterface.isBusy() )
-
-      //make sure we can close now
-      testCloseInteraction = new TestCloseInteraction( false )
-      Assert.assertTrue( dualPane.canClose( testCloseInteraction ) )
-
-      //since we just asked to close and we're NOT busy, make sure we did NOT prompt the user
-      Assert.assertFalse( testCloseInteraction.wasPromptedToConfirmClose )
-
-      gradleInterface.removeRequestObserver( testRequestObserver )
-  }
-
-   /**
-   * This tests that we can set a custom gradle executor.
-    */
-    @Test
-    public void testSettingCustomGradleExecutor()
-    {
-       DualPaneUIVersion1 dualPane = createDualPaneUI()
-      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
-
-      //it should be null by default
-      Assert.assertNull( gradleInterface.getCustomGradleExecutable() )
-
-      //now let's set it to a custom gradle executable. Actually, we're not going to really get
-      //a custom one; we'll use the normal one. Why? Because a real custom one would probably
-      //become a pain for maintaining this test. Here, we're interested that the basics are working
-      //from an open-api standpoint.
-      File gradleExecutor = getCustomGradleExecutable()
-
-      gradleInterface.setCustomGradleExecutable( gradleExecutor )
-
-      //make sure it was set
-      Assert.assertEquals( gradleExecutor, gradleInterface.getCustomGradleExecutable() )
-      Assert.assertEquals( gradleExecutor, dualPane.getCustomGradleExecutable() ) //just another way to get it
-
-      //add a request observer so we can observe when the command is finished.
-      TestRequestObserver testRequestObserver = new TestRequestObserver( RequestVersion1.REFRESH_TYPE )
-      gradleInterface.addRequestObserver( testRequestObserver )
-
-      //this starts the execution queue
-      dualPane.aboutToShow()
-
-      dualPane.refreshTaskTree()
-
-      //now we'll wait up to x seconds (arbitrary) for the task to execute. This is ugly, but its just a test.
-      int maximumWaitTime = 80;
-      int totalWaitTime = 0;
-      while ( testRequestObserver.request == null && totalWaitTime <= maximumWaitTime ) {
-          try {
-              Thread.sleep(1000);
-          }
-          catch (InterruptedException e) {
-              e.printStackTrace();
-          }
-
-          totalWaitTime += 1;
-      }
-
-      if( totalWaitTime > maximumWaitTime ) {
-        throw new AssertionFailedError("Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + gradleInterface.getCurrentDirectory() + "'\ngradle home: '" + gradleInterface.getGradleHomeDirectory() + "'\nOutput:\n" + testRequestObserver.output)
-      }
-
-      //make sure it completed execution correctly
-      Assert.assertEquals( "Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output , 0, testRequestObserver.result )
-
-      gradleInterface.removeRequestObserver( testRequestObserver )
-    }
-
-
-   /**
-    * This gets a gradle executable. That is, a way to launch gradle (shell script or batch file).
-    */
-    private File getCustomGradleExecutable()
-    {
-      //now let's set it to a custom gradle executable. We'll just point it to the regular
-      //gradle file (but it'll be the custom one.
-      String name;
-      String osName = System.getProperty("os.name").toLowerCase(Locale.US);
-      if (osName.toLowerCase().indexOf("windows") > -1)
-      {
-        name = "gradle.bat"
-      }
-      else
-      {
-        name = "gradle"
-      }
-
-      File gradleExecutor = new File( dist.getGradleHomeDir(), "bin/" + name )
-
-      //make sure the executable exists
-      Assert.assertTrue( "Missing gradle executable at: " + gradleExecutor, gradleExecutor.exists() )
-
-      return gradleExecutor
-    }
-}
-
-  /**
-   * Inner class for tracking a component's visiblity has changed.
-   * A HierarchyListener is how Swing notifies you that a component's visibility has changed.
-   * We'll use it to track if the component was shown and then hidden.
-   */
-    private class TestVisibilityHierarchyListener implements HierarchyListener
-    {
-      private boolean componentWasShown = false;
-      private boolean componentWasHidden = false;
-
-       public void hierarchyChanged(HierarchyEvent e)
-       {
-         if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED)!=0)
-         {
-            if( e.getComponent().isShowing() ) {
-              componentWasShown = true;
-            }
-            else {
-              componentWasHidden = true;
-            }
-         }
-       }
-     }
-
-
-
- /**
- * A class that manages a dummy gradle tab. It just consists of a label,
- *  but tracks that certain fields were called.
-  */
-  public class TestTab implements GradleTabVersion1
-  {
-    private JLabel label = new JLabel("Testing Testing 123")
-    private TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
-    private boolean nameRetrieved
-    private boolean informedAboutToShow
-    private boolean componentCreated
-
-    def TestTab() {
-      label.addHierarchyListener( hierarchyAdapter )
-    }
-
-    private void reset() {
-      label.removeHierarchyListener( hierarchyAdapter )         //remove the existing listener
-      hierarchyAdapter = new TestVisibilityHierarchyListener()  //create a new one
-      label.addHierarchyListener( hierarchyAdapter )            //add it
-
-      nameRetrieved = false
-      informedAboutToShow = false
-      componentCreated = false
-    }
-
-    String getName() {
-      nameRetrieved = true;
-      return "Test Tab";
-    }
-
-    Component createComponent() {
-      componentCreated = true;
-      return label;
-    }
-
-    void aboutToShow() {
-      informedAboutToShow = true;
-    }
-  }
-
-  /**
-  * This allows us to get a copy of hte request that was executed so we can inspect it when its done
-    */
-  private class TestRequestObserver implements RequestObserverVersion1
-  {
-    private String typeOfInterest;  //either RequestVersion1.EXECUTION_TYPE or RequestVersion1.REFRESH_TYPE
-    private RequestVersion1 request
-    private int result = -98 //means it hasn't been set to anything. 0 means success, so we have to initialize it to something else
-    private String output
-
-
-    def TestRequestObserver(typeOfInterest) {
-      this.typeOfInterest = typeOfInterest;
-    }
-
-    void executionRequestAdded(RequestVersion1 request) { }
-    void refreshRequestAdded(RequestVersion1 request) { }
-    void aboutToExecuteRequest(RequestVersion1 request) { }
-
-    void requestExecutionComplete(RequestVersion1 request, int result, String output) {
-      if( this.typeOfInterest.equals( request.getType() ) ) {  //refreshes will come through here. We're ignoring those
-        this.request = request
-        this.result = result
-        this.output = output
-      }
-    }
-
-  }
-
-  /**
-   * Class that tracks whether we were prompted to confirm close. It also returns a specific
-   * value to that prompt.
-   */
-    private class TestCloseInteraction implements BasicGradleUIVersion1.CloseInteraction
-    {
-      boolean wasPromptedToConfirmClose
-      boolean promptResult
-
-
-
-      def TestCloseInteraction(promptResult) {
-        this.promptResult = promptResult;
-      }
-
-      boolean promptUserToConfirmClosingWhileBusy() {
-        wasPromptedToConfirmClose = true
-        return promptResult
-      }
-    }
-
-  /**
-   * This appends a specified string to the command line when executing a command. 
-   */
-  private class TestCommandLineArgumentAlteringListenerVersion1 implements CommandLineArgumentAlteringListenerVersion1
-  {
-    private String additionalArguments;
-
-    def TestCommandLineArgumentAlteringListenerVersion1(additionalArguments) {
-      this.additionalArguments = additionalArguments;
-    }
-
-    String getAdditionalCommandLineArguments(String commandLineArguments) {
-      if( commandLineArguments.equals( "-s" ) ) {  //we're only interested in altering this one command
-        return additionalArguments;
-      }
-
-      return null;
-    }
-  }
-
-
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/OutputUILordTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/OutputUILordTest.groovy
deleted file mode 100644
index 6aeb90c..0000000
--- a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/OutputUILordTest.groovy
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests;
-
-
-import java.awt.Font
-import javax.swing.UIManager
-import junit.framework.AssertionFailedError
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.openapi.external.foundation.GradleInterfaceVersion1
-import org.gradle.openapi.external.foundation.RequestObserverVersion1
-import org.gradle.openapi.external.foundation.RequestVersion1
-import org.gradle.openapi.external.ui.OutputUILordVersion1
-import org.gradle.openapi.external.ui.SinglePaneUIVersion1
-import org.gradle.openapi.external.ui.UIFactory
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests aspects of the OutputUILord in OpenAPI
- *
- * @author mhunsicker
- */
- at RunWith(DistributionIntegrationTestRunner.class)
-public class OutputUILordTest  {
-  static final String JAVA_PROJECT_NAME = 'javaproject'
-  static final String SHARED_NAME = 'shared'
-  static final String API_NAME = 'api'
-  static final String WEBAPP_NAME = 'webservice'
-  static final String SERVICES_NAME = 'services'
-  static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
-  private File javaprojectDir
-
-  @Rule public final GradleDistribution dist = new GradleDistribution()
-  @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-  @Rule public final Sample sample = new Sample('java/quickstart')
-
-  @Before
-  void setUp() {
-      javaprojectDir = sample.dir
-  }
-
-  /**
-   * Helper function that creates a single pane UI
-   */
-   private SinglePaneUIVersion1 createSinglePaneUI()
-   {
-     TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1() )
-     SinglePaneUIVersion1 singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false )
-
-     //make sure we got something
-     Assert.assertNotNull( singlePane )
-
-     singlePane.setCurrentDirectory( javaprojectDir )
-
-     return singlePane
-   }
-
-  /**
-  * This verifies that you can add file extension to the output lord. This is for
-  * highlighting file links in the output. Here, we're just interested in whether
-  * or not the functions work via/exists in the Open API. The actual functionality
-  * is tested elsewhere.
-  */
-  @Test
-  public void testAddingFileExtension()
-  {
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-
-    outputUILord.addFileExtension( '.txt', ':' )
-    List extensions = outputUILord.getFileExtensions()
-    Assert.assertTrue( extensions.contains( '.txt' ) )
-  }
-
-  /**
-  * This verifies that you can add prefixed file extensions to the output lord. This
-  * is for highlighting file links in the output. Here, we're just interested in whether
-  * or not the functions work via/exists in the Open API. The actual functionality is tested elsewhere.
-  */
-  @Test
-  public void testAddingPrefixedFileLink()
-  {
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-
-    outputUILord.addPrefixedFileLink( "Error Text", "The error is:", ".txt", ":" )
-  }
-
-  /**
-  * This tests setting the font. There's not much here to do other than set it and then
-  * get it, making sure its the same. This isn't worried so much about the font itself as
-  * much as the open API doesn't have a problem with setting the font.
-  */
-  @Test
-  public void testFont()
-  {
-    if ( java.awt.GraphicsEnvironment.isHeadless() ) {
-       return;  // Can't run this test in headless mode!
-    }
-
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-    Font font = UIManager.getFont( "Button.font" )  //this specific font is not important
-
-    //make sure that the above font doesn't happen to be the default font for the output lord. If it
-    //is, this test will silently succeed even if it should fail.
-    Assert.assertNotSame( "Fonts are the same. This test is not setup correctly.", font, outputUILord.getOutputTextFont() )
-
-    //now set the new font and then make sure it worked
-    outputUILord.setOutputTextFont( font )
-  }
-
-  /**
-  *
-  */
-  @Test
-  public void testReExecute()
-  {
-    SinglePaneUIVersion1 singlePane = createSinglePaneUI()
-    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
-
-    //this starts the execution queue. This also initiates a refresh that we'll ignore later.
-    singlePane.aboutToShow()
-
-    TestRequestObserver2 testRequestObserver = new TestRequestObserver2()
-    singlePane.getGradleInterfaceVersion1().addRequestObserver( testRequestObserver )
-
-    //now execute a command
-    singlePane.executeCommand( "build", "test build")
-
-    //wait for it to complete
-    waitForCompletion( testRequestObserver, singlePane.getGradleInterfaceVersion1(), 80 )
-
-    //now the single command we're trying to test
-    outputUILord.reExecuteLastCommand();
-
-    //wait again for it exit
-    waitForCompletion( testRequestObserver, singlePane.getGradleInterfaceVersion1(), 80 )
-
-    //make sure it executed the correct request
-    Assert.assertEquals( "Incorrect request", "build", testRequestObserver.executionRequest.getFullCommandLine() )
-  }
-
-  private void waitForCompletion( TestRequestObserver2 testRequestObserver, GradleInterfaceVersion1 gradleInterface, int maximumWaitTime )
-  {
-    int totalWaitTime = 0;
-    while ( testRequestObserver.executionRequest == null && totalWaitTime <= maximumWaitTime ) {
-        try {
-            Thread.sleep(1000);
-        }
-        catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        totalWaitTime += 1;
-    }
-
-    if( totalWaitTime > maximumWaitTime ) {
-      throw new AssertionFailedError("Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + gradleInterface.getCurrentDirectory() + "'\ngradle home: '" + gradleInterface.getGradleHomeDirectory() + "'\nOutput:\n" + testRequestObserver.output)
-    }
-  }
-}
-
-    /**
-      * This allows us to get a copy of the request that was executed so we can inspect it when its done
-      */
-    private class TestRequestObserver2 implements RequestObserverVersion1
-    {
-      public RequestVersion1 executionRequest
-      public int result = -98 //means it hasn't been set to anything. 0 means success, so we have to initialize it to something else
-      public String output
-
-      void executionRequestAdded(RequestVersion1 request) { }
-      void refreshRequestAdded(RequestVersion1 request) { }
-      void aboutToExecuteRequest(RequestVersion1 request) { }
-
-      void requestExecutionComplete(RequestVersion1 request, int result, String output) {
-        if( RequestVersion1.EXECUTION_TYPE.equals( request.getType() ) ) {  //refreshes will come through here. We're ignoring those
-          this.executionRequest = request
-          this.result = result
-          this.output = output
-        }
-      }
-
-      public void reset() {
-        this.executionRequest = null;
-        this.result = -98;
-        this.output = null;
-      }
-
-    }
-
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestAlternateUIInteractionVersion1.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestAlternateUIInteractionVersion1.java
deleted file mode 100644
index ef5d2c2..0000000
--- a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestAlternateUIInteractionVersion1.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests;
-
-import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
-
-import java.io.File;
-
-/**
- * Implementation of AlternateUIInteractionVersion1 for testing purposes.
- * This would lend itself well for mocking. 
- *
- * @author mhunsicker
-  */
-public class TestAlternateUIInteractionVersion1 implements AlternateUIInteractionVersion1 {
-
-    private boolean supportsEditingOpeningFiles;
-
-    public TestAlternateUIInteractionVersion1() {
-    }
-
-    public TestAlternateUIInteractionVersion1(boolean supportsEditingOpeningFiles) {
-        this.supportsEditingOpeningFiles = supportsEditingOpeningFiles;
-    }
-
-    public void openFile(File file, int line) {
-
-    }
-
-    public void editFile(File file, int line) {
-
-    }
-
-    public boolean doesSupportEditingOpeningFiles() {
-        return supportsEditingOpeningFiles;
-    }
-
-    public void aboutToExecuteCommand(String fullCommandLine) {
-
-    }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestSettingsNodeVersion1.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestSettingsNodeVersion1.java
deleted file mode 100644
index 62032c4..0000000
--- a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestSettingsNodeVersion1.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests;
-
-import org.gradle.openapi.external.ui.SettingsNodeVersion1;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Implementation of settings node. It basically mirrors a DOM.
- * @author mhunsicker
- */
-public class TestSettingsNodeVersion1 implements SettingsNodeVersion1 {
-
-    private String name;
-    private String value;
-    private HashMap<String,String> attributes = new HashMap<String, String>();
-    private SettingsNodeVersion1 parent;
-    private List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
-
-
-    public TestSettingsNodeVersion1() {
-        //this creates a root settings node.
-    }
-
-    public TestSettingsNodeVersion1(SettingsNodeVersion1 parent) {
-        this.parent = parent;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setValue(String value) {
-        this.value = value;
-    }
-
-    public String getValue() {
-        return value;
-    }
-
-    public void setValueOfChild(String name, String value) {
-        SettingsNodeVersion1 settingsNode = addChildIfNotPresent(name);
-        settingsNode.setValue(value);
-    }
-
-    public String getValueOfChild(String name, String defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-            if (value != null) {
-                return value;
-            }
-        }
-        return defaultValue;
-    }
-
-    public SettingsNodeVersion1 getChildNode(String name) {
-        Iterator<SettingsNodeVersion1> iterator = children.iterator();
-        while (iterator.hasNext()) {
-            SettingsNodeVersion1 childNode = iterator.next();
-            if (name.equals(childNode.getName() )) {
-                return childNode;
-            }
-        }
-        return null;
-    }
-
-    public List<SettingsNodeVersion1> getChildNodes() {
-        return children;
-    }
-
-    public List<SettingsNodeVersion1> getChildNodes(String name) {
-        List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
-
-        Iterator<SettingsNodeVersion1> iterator = children.iterator();
-        while (iterator.hasNext()) {
-            SettingsNodeVersion1 childNode = iterator.next();
-            if (name.equals(childNode.getName()) ) {
-                children.add(childNode);
-            }
-        }
-
-        return children;
-    }
-
-    public int getValueOfChildAsInt(String name, int defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-
-            try {
-                if (value != null) {
-                    return Integer.parseInt(value);
-                }
-            } catch (NumberFormatException e) {
-                //we couldn't parse it. Just return the default.
-            }
-        }
-        return defaultValue;
-    }
-
-    public void setValueOfChildAsInt(String name, int value) {
-        setValueOfChild(name, Integer.toString(value));
-    }
-
-    public long getValueOfChildAsLong(String name, long defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-
-            try {
-                if (value != null) {
-                    return Long.parseLong(value);
-                }
-            } catch (NumberFormatException e) {
-                //we couldn't parse it. Just return the default.
-            }
-        }
-        return defaultValue;
-    }
-
-    public void setValueOfChildAsLong(String name, long value) {
-        setValueOfChild(name, Long.toString(value));
-    }
-
-    public boolean getValueOfChildAsBoolean(String name, boolean defaultValue) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode != null) {
-            String value = settingsNode.getValue();
-
-            //I'm not calling 'Boolean.parseBoolean( value )' because it will return false if the value isn't true/false
-            //and we want it to return whatever the default is if its not a boolean.
-            if (value != null) {
-                if ("true".equalsIgnoreCase(value)) {
-                    return true;
-                }
-
-                if ("false".equalsIgnoreCase(value)) {
-                    return false;
-                }
-            }
-        }
-
-        return defaultValue;
-    }
-
-    public void setValueOfChildAsBoolean(String name, boolean value) {
-        setValueOfChild(name, Boolean.toString(value));
-    }
-
-    public SettingsNodeVersion1 addChild(String name) {
-        SettingsNodeVersion1 childNode = new TestSettingsNodeVersion1( this );
-        childNode.setName( name );
-
-        children.add( childNode );
-        return childNode;
-    }
-
-    public SettingsNodeVersion1 addChildIfNotPresent(String name) {
-        SettingsNodeVersion1 settingsNode = getChildNode(name);
-        if (settingsNode == null) {
-            settingsNode = addChild(name);
-        }
-
-        return settingsNode;
-    }
-
-    public SettingsNodeVersion1 getNodeAtPath(String... pathPortions) {
-        if (pathPortions == null || pathPortions.length == 0) {
-            return null;
-        }
-
-        String firstPathPortion = pathPortions[0];
-
-        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
-
-        int index = 1; //Skip the first one. we've already used that one.
-        while (index < pathPortions.length && currentNode != null) {
-            String pathPortion = pathPortions[index];
-            currentNode = currentNode.getChildNode(pathPortion);
-            index++;
-        }
-
-        return currentNode;
-    }
-
-    private SettingsNodeVersion1 getNodeAtPathCreateIfNotFound(String... pathPortions) {
-        if (pathPortions == null || pathPortions.length == 0) {
-            return null;
-        }
-
-        String firstPathPortion = pathPortions[0];
-
-        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
-        if (currentNode == null) {
-            currentNode = addChild(firstPathPortion);
-        }
-
-        int index = 1;
-        while (index < pathPortions.length) {
-            String pathPortion = pathPortions[index];
-            currentNode = currentNode.getChildNode(pathPortion);
-            if (currentNode == null) {
-                currentNode = addChild(firstPathPortion);
-            }
-
-            index++;
-        }
-
-        return currentNode;
-    }
-
-    public void removeFromParent() {
-        ((TestSettingsNodeVersion1) this.parent).children.remove( this );
-        this.parent = null;
-    }
-
-    public void removeAllChildren() {
-        children.clear();
-    }
-
-    @Override
-    public String toString() {
-        return getName() + "='" + getValue() + "' " + children.size() + " children";
-    }
-}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestSingleDualPaneUIInteractionVersion1.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestSingleDualPaneUIInteractionVersion1.java
deleted file mode 100644
index bf5ad6a..0000000
--- a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/TestSingleDualPaneUIInteractionVersion1.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests;
-
-import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
-import org.gradle.openapi.external.ui.DualPaneUIInteractionVersion1;
-import org.gradle.openapi.external.ui.SettingsNodeVersion1;
-import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
-
-/**
- * This is a test implementation of both SinglePaneUIInteractionVersion1 and DualPaneUIInteractionVersion1.
- * This is really just a container to hand off more complex interactions to the UI when asked for.
- * @author mhunsicker
- */
-public class TestSingleDualPaneUIInteractionVersion1 implements SinglePaneUIInteractionVersion1, DualPaneUIInteractionVersion1 {
-
-    private AlternateUIInteractionVersion1 alternateUIInteractionVersion1;
-    private SettingsNodeVersion1 settingsNodeVersion1;
-
-    public TestSingleDualPaneUIInteractionVersion1(AlternateUIInteractionVersion1 alternateUIInteractionVersion1, SettingsNodeVersion1 settingsNodeVersion1) {
-        this.alternateUIInteractionVersion1 = alternateUIInteractionVersion1;
-        this.settingsNodeVersion1 = settingsNodeVersion1;
-    }
-
-    public AlternateUIInteractionVersion1 instantiateAlternateUIInteraction() {
-        return alternateUIInteractionVersion1;
-    }
-
-    public SettingsNodeVersion1 instantiateSettings() {
-        return settingsNodeVersion1;
-    }
-}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/BlockingRequestObserver.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/BlockingRequestObserver.java
new file mode 100644
index 0000000..8f9b051
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/BlockingRequestObserver.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+import org.gradle.openapi.external.foundation.RequestObserverVersion1;
+import org.gradle.openapi.external.foundation.RequestVersion1;
+import org.gradle.util.UncheckedException;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class BlockingRequestObserver implements RequestObserverVersion1 {
+    private final String typeOfInterest;  //either RequestVersion1.EXECUTION_TYPE or RequestVersion1.REFRESH_TYPE
+    private RequestVersion1 request;
+    private Integer result;
+    private String output;
+    private final Lock lock = new ReentrantLock();
+    private final Condition condition = lock.newCondition();
+    private Throwable failure;
+
+    public BlockingRequestObserver() {
+        this(RequestVersion1.EXECUTION_TYPE);
+    }
+
+    public BlockingRequestObserver(String typeOfInterest) {
+        this.typeOfInterest = typeOfInterest;
+    }
+
+    public RequestVersion1 getRequest() {
+        lock.lock();
+        try {
+            assertComplete();
+            return request;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public int getResult() {
+        lock.lock();
+        try {
+            assertComplete();
+            return result;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public String getOutput() {
+        lock.lock();
+        try {
+            assertComplete();
+            return output;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private void assertComplete() {
+        if (request == null) {
+            throw new AssertionError("Request has not completed.");
+        }
+    }
+
+    void reset() {
+        lock.lock();
+        try {
+            request = null;
+            result = null;
+            output = null;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void executionRequestAdded(RequestVersion1 request) {
+    }
+
+    public void refreshRequestAdded(RequestVersion1 request) {
+    }
+
+    public void aboutToExecuteRequest(RequestVersion1 request) {
+    }
+
+    public void requestExecutionComplete(RequestVersion1 request, int result, String output) {
+        if (this.typeOfInterest.equals(request.getType())) {
+            lock.lock();
+            try {
+                if (this.request != null) {
+                    failure = new AssertionError("Multiple results for request.");
+                }
+                this.request = request;
+                this.result = result;
+                this.output = output;
+                condition.signalAll();
+            } finally {
+                lock.unlock();
+            }
+        }
+    }
+
+    void waitForRequestExecutionComplete(int timeOutValue, TimeUnit timeOutUnits) {
+        lock.lock();
+        try {
+            Date expiry = new Date(System.currentTimeMillis() + timeOutUnits.toMillis(timeOutValue));
+            while (failure == null && request == null) {
+                if (!condition.awaitUntil(expiry)) {
+                    throw new AssertionError("Timeout waiting for request to complete.");
+                }
+            }
+            if (failure != null) {
+                throw failure;
+            }
+        } catch(Throwable t) {
+            throw UncheckedException.asUncheckedException(t);
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/CompatibilityIntegrationTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/CompatibilityIntegrationTest.groovy
new file mode 100644
index 0000000..9dfda94
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/CompatibilityIntegrationTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi
+
+import org.gradle.integtests.DistributionIntegrationTestRunner
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.PreviousGradleVersionExecuter
+import org.gradle.integtests.fixtures.TestResources
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.gradle.api.internal.AbstractClassPathProvider
+import org.gradle.integtests.fixtures.BasicGradleDistribution
+import org.junit.Assert
+import org.slf4j.LoggerFactory
+import org.slf4j.Logger
+
+ at RunWith(DistributionIntegrationTestRunner.class)
+class CompatibilityIntegrationTest {
+    private final Logger logger = LoggerFactory.getLogger(CompatibilityIntegrationTest)
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final TestResources resources = new TestResources()
+
+    private final PreviousGradleVersionExecuter gradle09rc1 = dist.previousVersion('0.9-rc-1')
+
+    @Test
+    public void canUseOpenApiFromCurrentVersionToBuildUsingAnOlderVersion() {
+        [gradle09rc1].each {
+            checkCanBuildUsing(dist, it)
+        }
+    }
+
+    @Test
+    public void canUseOpenApiFromOlderVersionToBuildUsingCurrentVersion() {
+        [gradle09rc1].each {
+            checkCanBuildUsing(it, dist)
+        }
+    }
+
+    def checkCanBuildUsing(BasicGradleDistribution openApiVersion, BasicGradleDistribution buildVersion) {
+        def testClasses = AbstractClassPathProvider.getClasspathForClass(CrossVersionBuilder.class)
+        def junitJar = AbstractClassPathProvider.getClasspathForClass(Assert.class)
+        def classpath = [testClasses, junitJar] + openApiVersion.gradleHomeDir.file('lib').listFiles().findAll { it.name =~ /gradle-open-api.*\.jar/ }
+        logger.info('Using Open API classpath {}', classpath)
+        def classloader = new URLClassLoader(classpath.collect { it.toURI().toURL() } as URL[], ClassLoader.systemClassLoader.parent)
+
+        def builder = classloader.loadClass(CrossVersionBuilder.class.name).newInstance()
+        builder.targetGradleHomeDir = buildVersion.gradleHomeDir
+        builder.currentDir = dist.testDir
+        builder.version = buildVersion.version
+        builder.build()
+    }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionBuilder.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionBuilder.java
new file mode 100644
index 0000000..71b7923
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/CrossVersionBuilder.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+import org.gradle.openapi.external.foundation.GradleInterfaceVersion2;
+import org.gradle.openapi.external.foundation.ProjectVersion1;
+import org.gradle.openapi.external.foundation.RequestVersion1;
+import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
+import org.gradle.openapi.external.ui.UIFactory;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class CrossVersionBuilder {
+    private File targetGradleHomeDir;
+    private File currentDir;
+    private String version;
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public File getCurrentDir() {
+        return currentDir;
+    }
+
+    public void setCurrentDir(File currentDir) {
+        this.currentDir = currentDir;
+    }
+
+    public File getTargetGradleHomeDir() {
+        return targetGradleHomeDir;
+    }
+
+    public void setTargetGradleHomeDir(File targetGradleHomeDir) {
+        this.targetGradleHomeDir = targetGradleHomeDir;
+    }
+
+    public void build() throws Exception {
+        assert targetGradleHomeDir != null;
+        assert currentDir != null;
+
+        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
+        SinglePaneUIVersion1 singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), targetGradleHomeDir, testSingleDualPaneUIInteractionVersion1, false);
+
+        String actualVersion = ((GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).getVersion();
+        assertEquals(version, actualVersion);
+
+        singlePane.setCurrentDirectory(currentDir);
+
+        GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1();
+
+        BlockingRequestObserver testRequestObserver = new BlockingRequestObserver(RequestVersion1.REFRESH_TYPE);
+        gradleInterface.addRequestObserver(testRequestObserver);
+
+        singlePane.aboutToShow();
+
+        gradleInterface.refreshTaskTree();
+
+        testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS);
+
+        assertEquals(String.format("Execution failed%n%s", testRequestObserver.getOutput()), 0, (long) testRequestObserver.getResult());
+
+        List<ProjectVersion1> rootProjects = gradleInterface.getRootProjects();
+        assertTrue(!rootProjects.isEmpty());
+
+        ProjectVersion1 rootProject = rootProjects.get(0);
+        assertEquals(3, rootProject.getSubProjects().size());
+
+        assertTrue(rootProject.getTasks().size() > 4);
+    }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/GradleRunnerTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/GradleRunnerTest.groovy
new file mode 100644
index 0000000..d291f1b
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/GradleRunnerTest.groovy
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi
+
+import junit.framework.AssertionFailedError
+import org.gradle.integtests.DistributionIntegrationTestRunner
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.openapi.external.runner.GradleRunnerFactory
+import org.gradle.openapi.external.runner.GradleRunnerInteractionVersion1
+import org.gradle.openapi.external.runner.GradleRunnerVersion1
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+ at RunWith(DistributionIntegrationTestRunner.class)
+class GradleRunnerTest {
+
+  static final String JAVA_PROJECT_NAME = 'javaproject'
+  static final String SHARED_NAME = 'shared'
+  static final String API_NAME = 'api'
+  static final String WEBAPP_NAME = 'webservice'
+  static final String SERVICES_NAME = 'services'
+  static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
+
+  private File javaprojectDir
+
+  @Rule public final GradleDistribution dist = new GradleDistribution()
+  @Rule public final TestResources resources = new TestResources('testproject')
+
+  @Before
+  void setUp() {
+      javaprojectDir = dist.testDir
+  }
+
+  /**
+   * We just want to make sure we can instantiate a GradleRunner here. That's all
+  */
+  @Test
+  public void testInstantiation()
+  {
+    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1(javaprojectDir)
+
+    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
+
+    Assert.assertNotNull( "Failed to instantiate runner", runner )
+  }
+
+  /**
+   * This does a basic execution. It also checks to make sure that the notifications were fired
+   * correctly.
+  */
+  @Test
+  public void testExecution()
+  {
+    TestGradleRunnerInteractionVersion1 interaction = new TestGradleRunnerInteractionVersion1( javaprojectDir )
+
+    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
+
+    Assert.assertNotNull( "Failed to instantiate runner", runner )
+
+    runner.executeCommand( "clean build" )
+
+        //wait for it to complete
+    int totalWaitTime = 0;
+    int maximumWaitTime = 80
+    while ( !interaction.executionFinished && totalWaitTime <= maximumWaitTime ) {
+        try {
+            Thread.sleep(1000);
+        }
+        catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        totalWaitTime += 1;
+    }
+
+    if( totalWaitTime > maximumWaitTime ) {
+      throw new AssertionFailedError( "Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + interaction.getWorkingDirectory() + "'\nOutput:\n" + interaction.output.toString() )
+    }
+
+    //now make sure we were notified of things correctly:
+
+    //it should have fired a message that execution has started
+    Assert.assertTrue( "Execution did not report started", interaction.executionStarted )
+    
+    //it should have finished
+    Assert.assertTrue( "Execution did not report finished", interaction.executionFinished )
+
+    //it should have been successful
+    Assert.assertTrue( "Did not execute command successfully", interaction.wasSuccessful )
+
+    //we should have output
+    Assert.assertTrue( "Missing output", interaction.output.length() > 0 )
+
+    //we should have a message when we finished (basically the full output)
+    Assert.assertTrue( "Missing finish message", interaction.finishMessage != null )
+
+    //there should have been multiple tasks to execute
+    Assert.assertTrue( "Not enough tasks executed. Expected multiple. Found " + interaction.numberOfTasksToExecute, interaction.numberOfTasksToExecute > 1 )
+
+    //we should have been notified that tasks started and completed (we're not interested in tracking how many times or specific tasks as that might change too often with releases of gradle.
+    Assert.assertTrue( "No tasks reported started", interaction.taskStarted )
+    Assert.assertTrue( "No tasks reported completed", interaction.taskCompleted )
+  }
+
+  /**
+   * This tests killing a task. We'll start a build task then kill it after it starts executing.
+   * Note: the kill interaction actually kills execution. It waits for a certain number of tasks
+   * to be executed.
+  */
+  @Test
+  public void testKill()
+  {
+    KillTestInteraction interaction = new KillTestInteraction(javaprojectDir)
+
+    GradleRunnerVersion1 runner = GradleRunnerFactory.createGradleRunner(getClass().getClassLoader(), dist.getGradleHomeDir(), interaction, true)
+
+    interaction.runner = runner
+
+    Assert.assertNotNull( "Failed to instantiate runner", runner )
+
+    runner.executeCommand( "build" )
+
+        //wait for it to complete
+    int totalWaitTime = 0;
+    int maximumWaitTime = 80
+    while ( !interaction.executionFinished && totalWaitTime <= maximumWaitTime ) {
+        try {
+            Thread.sleep(1000);
+        }
+        catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        totalWaitTime += 1;
+    }
+
+    if( totalWaitTime > maximumWaitTime ) {
+      throw new AssertionFailedError( "Waited " + totalWaitTime + " seconds and failed to finish executing command. This is taking too long, so assuming something is wrong.\nCurrent project directory: '" + interaction.getWorkingDirectory() + "'\nOutput:\n" + interaction.output.toString() )
+    }
+
+    //make sure we tried to kill the task
+    Assert.assertTrue( "Did not attempt to kill execution", interaction.killedTask )
+
+    //now make sure we were notified of things correctly:
+
+    //it should NOT have been successful
+    Assert.assertFalse( "Erroneously executed successfully (was not killed)", interaction.wasSuccessful )
+
+    //it should have fired a message that execution has started
+    Assert.assertTrue( "Execution did not report started", interaction.executionStarted )
+
+    //it should have finished
+    Assert.assertTrue( "Execution did not report finished", interaction.executionFinished )
+  }
+}
+
+  //Inner class used to track what has been called
+  public class TestGradleRunnerInteractionVersion1 implements GradleRunnerInteractionVersion1
+  {
+    private File workingDirectory
+    private StringBuilder output = new StringBuilder()
+    private String finishMessage
+    boolean wasSuccessful
+    boolean executionStarted
+    int numberOfTasksToExecute
+    boolean executionFinished
+    boolean taskCompleted
+    boolean taskStarted
+
+
+    public TestGradleRunnerInteractionVersion1(File workingDirectory) {
+      this.workingDirectory = workingDirectory;
+    }
+
+    def TestGradleRunnerInteractionVersion1() {
+    }
+
+    File getWorkingDirectory() { return workingDirectory }
+
+    GradleRunnerInteractionVersion1.LogLevel getLogLevel() { return GradleRunnerInteractionVersion1.LogLevel.Lifecycle }
+
+    GradleRunnerInteractionVersion1.StackTraceLevel getStackTraceLevel() { return GradleRunnerInteractionVersion1.StackTraceLevel.InternalExceptions }
+
+    void reportExecutionStarted() { executionStarted = true }
+    void reportNumberOfTasksToExecute(int size) { numberOfTasksToExecute = size }
+
+    //both of these will be fired often. We're not going to try to track that.
+    void reportTaskStarted(String currentTaskName, float percentComplete) { taskStarted = true; }
+    void reportTaskComplete(String currentTaskName, float percentComplete) { taskCompleted = true }
+
+    void reportLiveOutput(String output) { this.output.append( output ) }
+
+    void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
+      this.executionFinished = true;
+      this.wasSuccessful = wasSuccessful
+      this.finishMessage = message;
+    }
+
+    File getCustomGradleExecutable() { return null; }
+  }
+
+
+  //class to track that has class was started and then kills it. 
+  private class KillTestInteraction implements GradleRunnerInteractionVersion1
+  {
+    private GradleRunnerVersion1 runner
+    int tasks = 0
+    boolean killedTask
+
+    //after at least 2 tasks start, try to kill the process. This simulates someone killing it while
+    //its in the middle of running
+    def void reportTaskStarted(String currentTaskName, float percentComplete) {
+      tasks++
+      if( tasks == 2 ) {
+        killedTask = true
+        runner.killProcess();
+      }
+    }
+    private File workingDirectory
+    boolean wasSuccessful
+    boolean executionStarted
+    boolean executionFinished
+
+    public KillTestInteraction(File workingDirectory) {
+      this.workingDirectory = workingDirectory;
+    }
+
+    File getWorkingDirectory() { return workingDirectory }
+
+    GradleRunnerInteractionVersion1.LogLevel getLogLevel() { return GradleRunnerInteractionVersion1.LogLevel.Lifecycle }
+
+    GradleRunnerInteractionVersion1.StackTraceLevel getStackTraceLevel() { return GradleRunnerInteractionVersion1.StackTraceLevel.InternalExceptions }
+
+    void reportExecutionStarted() { executionStarted = true }
+    void reportNumberOfTasksToExecute(int size) { }
+
+    void reportTaskComplete(String currentTaskName, float percentComplete) {}
+    void reportLiveOutput(String output) {}
+    File getCustomGradleExecutable() { return null; }
+
+    void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
+      this.executionFinished = true;
+      this.wasSuccessful = wasSuccessful
+    }
+  }
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiFixture.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiFixture.java
new file mode 100644
index 0000000..239ca5b
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiFixture.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+import org.gradle.integtests.fixtures.GradleDistribution;
+import org.gradle.integtests.fixtures.RuleHelper;
+import org.gradle.openapi.external.ui.CommandLineArgumentAlteringListenerVersion1;
+import org.gradle.openapi.external.ui.DualPaneUIVersion1;
+import org.gradle.openapi.external.ui.SinglePaneUIVersion1;
+import org.gradle.openapi.external.ui.UIFactory;
+import org.gradle.util.UncheckedException;
+import org.junit.Assert;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class OpenApiFixture implements MethodRule {
+    private GradleDistribution dist;
+    private final List<JFrame> frames = new ArrayList<JFrame>();
+
+    public Statement apply(final Statement base, FrameworkMethod method, final Object target) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                dist = RuleHelper.getField(target, GradleDistribution.class);
+                try {
+                    base.evaluate();
+                } finally {
+                    SwingUtilities.invokeAndWait(new Runnable() {
+                        public void run() {
+                            for (JFrame frame : frames) {
+                                frame.dispose();
+                            }
+                        }
+                    });
+                }
+            }
+        };
+    }
+
+    public SinglePaneUIVersion1 createSinglePaneUI() {
+        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
+        SinglePaneUIVersion1 singlePane;
+        try {
+            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false);
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+
+        //make sure we got something
+        Assert.assertNotNull(singlePane);
+
+        singlePane.setCurrentDirectory(dist.getTestDir());
+        singlePane.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(dist.getUserHomeDir()));
+
+        return singlePane;
+    }
+
+    public DualPaneUIVersion1 createDualPaneUI() {
+        TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1(new TestAlternateUIInteractionVersion1(), new TestSettingsNodeVersion1());
+        DualPaneUIVersion1 dualPane;
+        try {
+            dualPane = UIFactory.createDualPaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false);
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+
+        //make sure we got something
+        Assert.assertNotNull(dualPane);
+
+        dualPane.setCurrentDirectory(dist.getTestDir());
+        dualPane.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(dist.getUserHomeDir()));
+
+        return dualPane;
+    }
+
+    public JFrame open(SinglePaneUIVersion1 ui) {
+        return createTestFrame(ui.getComponent(), null);
+    }
+
+    public JFrame open(DualPaneUIVersion1 ui) {
+        return createTestFrame(ui.getMainComponent(), ui.getOutputPanel());
+    }
+
+    /*
+    * This shows the specified frame for a moment so the event queue can be emptied. This
+    * ensures Swing events a processed
+    * Don't place anything between the following three lines (especially something that might
+    * throw an exception). This shows and hides the UI, giving it time to actually show itself
+    * and empty the event dispatch queue.
+    */
+    public void flushEventQueue(final JFrame frame) {
+        try {
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    frame.setVisible(true);
+                }
+            });
+            Thread.sleep(500);
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    frame.setVisible(false);
+                }
+            });
+        } catch (Exception e) {
+            throw UncheckedException.asUncheckedException(e);
+        }
+    }
+
+    private JFrame createTestFrame(Component mainComponent, Component outputComponent) {
+        JFrame frame = new JFrame();
+        frames.add(frame);
+        frame.setSize(650, 500);
+        JPanel panel = new JPanel(new BorderLayout());
+        frame.getContentPane().add(panel);
+
+        //add a large red label explaining this
+        JLabel label2 = new JLabel("Performing Open API Integration Test!");
+        label2.setForeground(Color.red);
+        label2.setFont(label2.getFont().deriveFont(26f));
+        panel.add(label2, BorderLayout.NORTH);
+
+        //add the main UI to the frame
+        panel.add(mainComponent, BorderLayout.CENTER);
+
+        if (outputComponent != null) {
+            panel.add(outputComponent, BorderLayout.SOUTH);
+        }
+
+        return frame;
+    }
+
+    private static class ExtraTestCommandLineOptionsListener implements CommandLineArgumentAlteringListenerVersion1 {
+        private final File gradleUserHomeDir;
+
+        public ExtraTestCommandLineOptionsListener(File gradleUserHomeDir) {
+            this.gradleUserHomeDir = gradleUserHomeDir;
+        }
+
+        public String getAdditionalCommandLineArguments(String commandLineArguments) {
+            return String.format("--no-search-upward --gradle-user-home \'%s\'", gradleUserHomeDir);
+        }
+    }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiUiTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiUiTest.groovy
new file mode 100644
index 0000000..6b6772b
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OpenApiUiTest.groovy
@@ -0,0 +1,986 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi
+
+import java.awt.Component
+import java.awt.event.HierarchyEvent
+import java.awt.event.HierarchyListener
+import java.util.concurrent.TimeUnit
+import javax.swing.JFrame
+import javax.swing.JLabel
+import junit.framework.AssertionFailedError
+import org.gradle.integtests.DistributionIntegrationTestRunner
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.openapi.external.ExternalUtility
+import org.gradle.openapi.external.foundation.GradleInterfaceVersion2
+import org.gradle.openapi.external.foundation.ProjectVersion1
+import org.gradle.openapi.external.foundation.RequestVersion1
+import org.gradle.openapi.external.foundation.TaskVersion1
+import org.gradle.openapi.external.foundation.favorites.FavoriteTaskVersion1
+import org.gradle.openapi.external.foundation.favorites.FavoritesEditorVersion1
+import org.gradle.util.OperatingSystem
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.gradle.openapi.external.ui.*
+import static org.hamcrest.Matchers.*
+
+/**
+ * This tests numerous aspects of the Open API UI. This is how the Idea plugin extracts the UI from
+ * Gradle.
+ */
+ at RunWith(DistributionIntegrationTestRunner.class)
+public class OpenApiUiTest {
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final TestResources resources = new TestResources('testproject')
+    @Rule public final OpenApiFixture openApi = new OpenApiFixture()
+
+    /**
+     This tests to see if we can call the UIFactory to create a single pane UI.
+     This is only testing that extracting the UI returns something without giving
+     errors and that it has a component. This is just a good general-case test
+     to make sure the basics are working.
+     */
+    @Test
+    public void testSinglePaneBasic() {
+        SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+        //make sure we got something
+        Assert.assertNotNull(singlePane)
+
+        //tell it we're about to show it, so it'll create a component
+        singlePane.aboutToShow();
+
+        //make sure we now have that component
+        Assert.assertNotNull(singlePane.getComponent())
+    }
+
+  /**
+   * This tests to see if we can call the UIFactory to create a dual pane UI.
+      This is only testing that extracting the UI returns something without giving
+      errors and that it has its dual components. This is just a good general-case test
+      to make sure the basics are working.
+   */
+    @Test
+    public void testDualPaneBasic()
+    {
+        DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+
+        //make sure we got something
+        Assert.assertNotNull( dualPane )
+
+        //tell it we're about to show it, so it'll create a component
+        dualPane.aboutToShow();
+
+        //make sure we now have the main component
+        Assert.assertNotNull( dualPane.getMainComponent() )
+
+        //and the output component
+        Assert.assertNotNull( dualPane.getOutputPanel() )
+    }
+
+    /**
+    * This verifies that favorites are working for some basics. We're going to test this with both
+     * the single and dual pane UIs (they actually use the same editor then for other tests we'll
+     * assume they're same).
+    */
+    @Test
+    public void testFavoritesBasic()
+    {
+      SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+      checkFavoritesBasic( singlePane )
+
+      DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+      checkFavoritesBasic( dualPane )
+    }
+
+    /**
+    * This verifies that we favorites are basically working based on the given UI. We're going to add one, then
+    * do some 'gets' to find the just-added favorite.
+    */
+    private void checkFavoritesBasic( BasicGradleUIVersion1 basicGradleUI )
+    {
+      FavoritesEditorVersion1 editor = basicGradleUI.getFavoritesEditor()
+
+      //there should be no favorites as of yet
+      Assert.assertTrue( editor.getFavoriteTasks().isEmpty() )
+
+      //add one (doesn't really matter what it is)
+      def fullCommandLine = "-t -S"
+      def displayName = "Task List With Stack trace"
+      FavoriteTaskVersion1 favorite = editor.addFavorite( fullCommandLine, displayName, true )
+
+      //make sure something was added
+      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
+
+      //get the newly-added favorite by command line.
+      FavoriteTaskVersion1 matchingFavorite1 = editor.getFavorite( fullCommandLine )
+      Assert.assertEquals( favorite, matchingFavorite1 )
+
+      //get the newly-added favorite by displayName.
+      FavoriteTaskVersion1 matchingFavorite2 = editor.getFavoriteByDisplayName( displayName )
+      Assert.assertEquals( favorite, matchingFavorite2 )
+
+      Assert.assertTrue( matchingFavorite2.alwaysShowOutput() )
+    }
+
+    /**
+    * This verifies that we can edit favorites. We're going to add a favorite then edit its
+     * command line, display name, and 'show output' setting.
+    */
+    @Test
+    public void testEditingFavorites()
+    {
+      SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+      FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
+
+      def originalFullCommandLine = "-t -S"
+      def originalDisplayName = "Task List With Stack trace"
+      FavoriteTaskVersion1 addedFavorite = editor.addFavorite(originalFullCommandLine, originalDisplayName, true)
+
+      //make sure we can find the just-added favorite
+      Assert.assertNotNull( editor.getFavorite( originalFullCommandLine ) )
+      Assert.assertNotNull( editor.getFavoriteByDisplayName( originalDisplayName ) )
+
+      String newFullCommandLine = "-t -S -d"
+      String newDisplayName = "new task list"
+      String error = editor.editFavorite( addedFavorite, newFullCommandLine, newDisplayName, false )
+      Assert.assertNull( error )  //we should get no error
+
+      //now we shouldn't be able to find the favorite using the original values. This is part of verifying the values were in fact changed.
+      Assert.assertNull( editor.getFavorite( originalFullCommandLine ) )
+      Assert.assertNull( editor.getFavoriteByDisplayName( originalDisplayName ) )
+
+      //make sure we can find it using the new values. This is part of verifying the values were in fact changed.
+      Assert.assertNotNull( editor.getFavorite( newFullCommandLine ) )
+      Assert.assertNotNull( editor.getFavoriteByDisplayName( newDisplayName ) )
+
+      //there should just be 1 favorite
+      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
+    }
+
+    /**
+    * This verifies that we can remove favorites. We're going to add some favorites then remove them
+     * verifying that they've gone.
+    */
+    @Test
+    public void testRemovingFavorites()
+    {
+      SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+      FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
+
+      //there should be no favorites as of yet
+      Assert.assertTrue( editor.getFavoriteTasks().isEmpty() )
+
+      //add one (doesn't really matter what it is)
+      String command1 = "build"
+      FavoriteTaskVersion1 favorite1 = editor.addFavorite( command1, command1, true )
+
+      //make sure it was added
+      Assert.assertNotNull( editor.getFavorite( command1 ) )
+      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
+
+      //add another one
+      String command2 = "build -xtest"
+      FavoriteTaskVersion1 favorite2 = editor.addFavorite( command2, command2, true )
+
+      //make sure it was added
+      Assert.assertNotNull( editor.getFavorite( command2 ) )
+      Assert.assertEquals( 2, editor.getFavoriteTasks().size() )
+
+      String command3 = "clean"
+      FavoriteTaskVersion1 favorite3 = editor.addFavorite( command3, command3, true )
+
+      //make sure it was added
+      Assert.assertNotNull( editor.getFavorite( command3 ) )
+      Assert.assertEquals( 3, editor.getFavoriteTasks().size() )
+
+      String command4 = "docs"
+      FavoriteTaskVersion1 favorite4 = editor.addFavorite( command4, command4, true )
+
+      //make sure it was added
+      Assert.assertNotNull( editor.getFavorite( command4 ) )
+      Assert.assertEquals( 4, editor.getFavoriteTasks().size() )
+
+      //now remove one of them
+      List removed1 = [ favorite2 ]
+      editor.removeFavorites( removed1 )
+
+      //make sure it was removed
+      Assert.assertNull( editor.getFavorite( command2 ) )
+      Assert.assertEquals( 3, editor.getFavoriteTasks().size() )
+
+      //now remove multiples
+      List removed2 = [ favorite1, favorite4 ]
+      editor.removeFavorites( removed2 )
+
+      //make sure they were both removed
+      Assert.assertNull( editor.getFavorite( command1 ) )
+      Assert.assertNull( editor.getFavorite( command4 ) )
+      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
+    }
+
+    /**
+   * This tests executing multiple favorites at once. We'll add two favorites, then execute both of them
+   * via GradleInterfaceVersion1.executeFavorites(). This should execute both as a single command
+   * concatenating them together.
+   */
+    @Test
+    public void testExecutingFavorites()
+    {
+      SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+      FavoritesEditorVersion1 editor = singlePane.getFavoritesEditor()
+
+      //this starts the execution queue
+      singlePane.aboutToShow()
+
+      //there should be no favorites as of yet
+      Assert.assertTrue( editor.getFavoriteTasks().isEmpty() )
+
+      //add one (doesn't really matter what it is)
+      String command1 = "build"
+      FavoriteTaskVersion1 favorite1 = editor.addFavorite( command1, command1, true )
+
+      //make sure it was added
+      Assert.assertNotNull( editor.getFavorite( command1 ) )
+      Assert.assertEquals( 1, editor.getFavoriteTasks().size() )
+
+      //add another one
+      String command2 = "clean"
+      FavoriteTaskVersion1 favorite2 = editor.addFavorite( command2, command2, true )
+
+      //make sure it was added
+      Assert.assertNotNull( editor.getFavorite( command2 ) )
+      Assert.assertEquals( 2, editor.getFavoriteTasks().size() )
+
+      //add a request observer so we can observe when the command is finished. This allows us to
+      //see what was actually executed.
+      BlockingRequestObserver testRequestObserver = new BlockingRequestObserver( RequestVersion1.EXECUTION_TYPE )
+      ((GradleInterfaceVersion2)singlePane.getGradleInterfaceVersion1()).addRequestObserver( testRequestObserver )
+
+      //now execute both favorites
+      List<FavoriteTaskVersion1> favorites = [ favorite1, favorite2 ]
+      RequestVersion1 request = ( (GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1() ).executeFavorites(favorites)
+
+      Assert.assertNotNull( request )
+
+      //verify that the actual command that was executed is a concatenation of both favorites
+      Assert.assertThat( request.getFullCommandLine(), startsWith("build clean") )
+    }
+
+    /**
+    * This tests getting projects and tasks from gradle. It then goes through a series of tests
+    * related to projects and tasks (such as making sure their description is returned, their
+    * parent is returned, etc).
+    */
+    @Test
+    public void testProjectsAndTasks()
+    {
+      DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
+
+      //make sure our samples directory exists
+      Assert.assertTrue( gradleInterface.getCurrentDirectory().isDirectory() )
+
+      //add a request observer so we can observe when the command is finished. This allows us to
+      //see what was actually executed.
+      BlockingRequestObserver testRequestObserver = new BlockingRequestObserver( RequestVersion1.REFRESH_TYPE )
+      gradleInterface.addRequestObserver( testRequestObserver )
+
+      //this starts the execution queue
+      dualPane.aboutToShow()
+
+      gradleInterface.refreshTaskTree()
+
+      testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+      Assert.assertEquals( "Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
+
+      List<ProjectVersion1> rootProjects = gradleInterface.getRootProjects();
+      Assert.assertFalse( rootProjects.isEmpty() );   //do we have any root projects?
+
+      ProjectVersion1 rootProject = rootProjects.get( 0 );
+      Assert.assertNotNull( rootProject );
+      Assert.assertThat( rootProject.getSubProjects().size(), equalTo(3))
+
+      //Quick check to make sure there are tasks on each of the sub projects.
+      //The exact task names will change over time, so I don't want to try
+      //to test for those. I'll just make sure there are several.
+      Iterator<ProjectVersion1> iterator = rootProjects.get(0).getSubProjects().iterator();
+      while( iterator.hasNext() )
+      {
+         ProjectVersion1 projectVersion1 = iterator.next();
+         Assert.assertTrue( projectVersion1.getTasks().size() > 4 );
+      }
+
+      //there should be a 'services' project
+      ProjectVersion1 servicesProject = rootProjects.get(0).getSubProject("services" );
+      Assert.assertNotNull( servicesProject );
+
+      //and it contains a 'webservice' sub project
+      ProjectVersion1 webserviceProject = servicesProject.getSubProject("webservice");
+      Assert.assertNotNull( webserviceProject );
+
+      ProjectVersion1 apiProject = rootProjects.get(0).getSubProject("api");
+      Assert.assertNotNull( apiProject );
+
+      //verify the parent project is set correctly
+      Assert.assertEquals( servicesProject, webserviceProject.getParentProject() )
+
+      //verify its full name is correct (this might should be prefixed with a colon)
+      Assert.assertEquals( "services:webservice", webserviceProject.getFullProjectName() )
+
+      //verify getSubProjectFromFullPath works
+      ProjectVersion1 foundProject = rootProject.getSubProjectFromFullPath("services:webservice")
+      Assert.assertNotNull( "Failed to find services:webservice", foundProject )
+      Assert.assertEquals( webserviceProject, foundProject )
+
+      //verify that are multiple tasks here (we know their should be)
+      Assert.assertTrue( webserviceProject.getTasks().size() > 4 );
+
+      //verify getTaskFromFullPath works
+      TaskVersion1 apiBuildTask = rootProject.getTaskFromFullPath(":api:build")
+      Assert.assertNotNull( "Failed to find :api:build", apiBuildTask )
+
+      Assert.assertEquals( apiProject, apiBuildTask.getProject() )
+
+      //then make sure it has a description
+      Assert.assertNotNull( apiBuildTask.getDescription() )
+
+      //and that its not marked as the default (we need a task to be the default so we can verify it returns true)
+      Assert.assertFalse( apiBuildTask.isDefault() )
+
+      //there are no default tasks here
+      Assert.assertTrue( apiProject.getDefaultTasks().isEmpty() )
+
+      //this build task is a child of the api project. Should be the same task we got earlier
+      TaskVersion1 buildTask = apiProject.getTask("build")
+      Assert.assertNotNull( "Failed to find build task", buildTask )
+      Assert.assertEquals( apiBuildTask, buildTask )
+    }
+
+   /**
+    * This verifies that the GradleInterfaceVersion1.refreshTaskTree that takes
+    * additional arguments works. We're not really interested in what those additional
+    * arguments are, just that it passes them along.
+    */
+    @Test
+    public void testRefreshWithArguments()
+    {
+      DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
+
+      //make sure our samples directory exists
+      if( !gradleInterface.getCurrentDirectory().exists() ) {
+        throw new AssertionFailedError('sample project missing. Expected it at: ' + gradleInterface.getCurrentDirectory())
+      }
+
+      //this starts the execution queue
+      dualPane.aboutToShow()
+
+      //add a request observer so we can observe when the command is finished. This allows us to
+      //see what was actually executed.
+      BlockingRequestObserver testRequestObserver = new BlockingRequestObserver( RequestVersion1.REFRESH_TYPE )
+      gradleInterface.addRequestObserver( testRequestObserver )
+
+      RequestVersion1 request = gradleInterface.refreshTaskTree2("-xtest")
+
+      //make sure that the actual request is the normal refresh request with our
+      //(this line is really what we're trying to test)
+      Assert.assertThat( request.getFullCommandLine(), startsWith("tasks -xtest") )
+
+      testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+      Assert.assertEquals( "Execution Failed: " + testRequestObserver.output, 0, testRequestObserver.result)
+
+      Assert.assertEquals( "Not our request", request, testRequestObserver.request );
+    }
+
+    /**
+    * This verifies that you can add custom stuff to the setup tab. This is a UI test and is kinda tricky. We're going
+    * to use a HierarchyListener to see if our component is made visible. This will confirm if it was added or not
+    * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
+    * really doing here, is adding a 'custom' component to the UI, then adding the UI to a frame, then showing the frame,
+    * so we can verify that our component was shown.
+    */
+    @Test
+    public void testAddingComponentToSetupTab()
+    {
+      if ( java.awt.GraphicsEnvironment.isHeadless() ) {
+        return;  // Can't run this test in headless mode!
+       }
+
+      JLabel label = new JLabel("Testing Testing 123")
+      TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
+      label.addHierarchyListener( hierarchyAdapter )
+
+      SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+      //make sure we haven't been told the component was shown or hidden yet
+      Assert.assertFalse( hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( hierarchyAdapter.componentWasHidden )
+
+      singlePane.aboutToShow();
+
+      singlePane.setCustomPanelToSetupTab( label )
+
+      //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
+      Assert.assertFalse( hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( hierarchyAdapter.componentWasHidden )
+
+      //now create a frame, place the UI in it, then show it briefly
+      JFrame frame = openApi.open(singlePane)
+
+      //set the Setup tab as the current tab. This is required to actually show the component.
+      int setupTabIndex = singlePane.getGradleTabIndex( "Setup" );
+      Assert.assertTrue( "Failed to get index of setup tab", setupTabIndex != -1 )
+      singlePane.setCurrentGradleTab( setupTabIndex );
+
+      //still should not show the component (its not yet visible, but is about to be)
+      Assert.assertFalse( hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( hierarchyAdapter.componentWasHidden )
+
+
+      //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
+      //queue. This is required for the setup tab to become current as well as show the custom component we added.
+      openApi.flushEventQueue( frame )
+
+      Assert.assertEquals( "The setup tab was not selected", setupTabIndex, singlePane.getCurrentGradleTab() )
+
+      //now the label should have been made visible then invisible
+      Assert.assertTrue( hierarchyAdapter.componentWasShown )
+      Assert.assertTrue( hierarchyAdapter.componentWasHidden )
+    }
+
+    /**
+    * This verifies that you can add a custom tab to the UI. This is a UI test and is kinda tricky. We're going
+    * to use a HierarchyListener to see if our tab component is made visible. This will confirm if it was added or not
+    * because it must be added to be made visible. To do this, however, we'll need to actually show the UI. All we're
+    * really doing here, is adding a tab to the UI, then adding the UI to a frame, then showing the frame so we can
+    * then verify that our tab was shown (actually using our tab's component).
+    */
+    @Test
+    public void testAddingCustomTab()
+    {
+      if ( java.awt.GraphicsEnvironment.isHeadless() ) {
+        return;  // Can't run this test in headless mode!
+      }
+
+      TestTab testTab = new TestTab()
+
+      SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+      //make sure we haven't been told the component was shown or hidden yet
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
+
+      //make sure things are initialized properly. These should all be false
+      Assert.assertFalse( testTab.nameRetrieved );
+      Assert.assertFalse( testTab.informedAboutToShow );
+      Assert.assertFalse( testTab.componentCreated );
+
+      int originalCount = singlePane.getGradleTabCount();
+
+      singlePane.addTab( 99, testTab ) //I don't really care about the index. It should accept a number that is too large and handle it appropriately.
+
+      singlePane.aboutToShow()
+
+      //this still should not show the component (at this point, we're probably more testing that our hierarchyAdapter is working)
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
+
+      //now create a frame, place the UI in it, then show it briefly
+      JFrame frame = openApi.open( singlePane )
+
+      String testTabName = "Test Tab"
+
+      //set the test tab as the current tab. This is required to actually show the component.
+      int testTabIndex = singlePane.getGradleTabIndex( testTabName )
+      Assert.assertTrue( "Failed to get index of test tab", testTabIndex != -1 )
+      singlePane.setCurrentGradleTab( testTabIndex )
+
+      //just to test getGradleTabName, make sure it returns our tab name
+      Assert.assertEquals( testTabName, singlePane.getGradleTabName( testTabIndex ) )
+
+      //to test getGradleTabCount, make sure the tab count went up by 1
+      Assert.assertEquals( originalCount + 1, singlePane.getGradleTabCount() )
+
+      //still should not show the component (its not yet visible, but is about to be)
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
+
+      //This shows and hides the UI, giving it time to actually show itself and empty the event dispatch
+      //queue. This is required for the test tab to become current.
+      openApi.flushEventQueue( frame )
+
+      Assert.assertEquals( "The test tab was not selected", testTabIndex, singlePane.getCurrentGradleTab() )
+
+      //now the label should have been made visible then invisible
+      Assert.assertTrue( testTab.hierarchyAdapter.componentWasShown )
+      Assert.assertTrue( testTab.hierarchyAdapter.componentWasHidden )
+
+      //at the end, the name should have been queried, we should have been told we were about to shown, and the component should be created
+      Assert.assertTrue( testTab.nameRetrieved );
+      Assert.assertTrue( testTab.informedAboutToShow );
+      Assert.assertTrue( testTab.componentCreated );
+
+      //reset the test tab (resets the listener so we can remove the tab and verify that it no longer shows up, as well as some of our test variables)
+      testTab.reset()
+      singlePane.removeTab( testTab )
+
+      //I'm going to set the current tab, but this shouldn't do anything because the tab was removed
+      singlePane.setCurrentGradleTab( testTabIndex );
+
+      //part of showing the UI is telling it its about to be shown. In this case, nothing should happen
+      //related to the test tab. It has been removed
+      singlePane.aboutToShow()
+
+      //This shows and hides the UI, giving it time to actually show itself and empty the event
+      //dispatch queue. This is required for the test tab to become current (were it still present).
+      openApi.flushEventQueue( frame )
+
+      //try to get the test tab
+      testTabIndex = singlePane.getGradleTabIndex( "Test Tab" );
+      Assert.assertTrue( "Erroneously got index of test tab. It was removed", testTabIndex == -1 )
+
+      //we've removed it, so it shouldn't have been polled about or informed of anything
+      Assert.assertFalse( testTab.nameRetrieved );
+      Assert.assertFalse( testTab.informedAboutToShow );
+      Assert.assertFalse( testTab.componentCreated );
+
+      //It was not shown after the reset, these should both be false
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasShown )
+      Assert.assertFalse( testTab.hierarchyAdapter.componentWasHidden )
+    }
+
+    /**
+    * We want to make sure the settings are working correctly here. This is the mechanism that
+     * handles saving/restoring the values within the UI and can be stored in different ways
+     * depending on how the UI integrated with its parent (its up to whoever implements
+     * SettingsNodeVersion1). Here, to spot check that the basics are working, we'll create a
+     * UI, set a value, close it, then recreate it using the same settings object. The values
+     * should be saved upon close and then restored.
+    */
+    @Test
+    public void testSettings()
+    {
+      TestSettingsNodeVersion1 settingsNode = new TestSettingsNodeVersion1();
+
+      TestSingleDualPaneUIInteractionVersion1 testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), settingsNode );
+        SinglePaneUIVersion1 singlePane = null;
+        try {
+            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false );
+        } catch (Exception e) {
+            throw new AssertionFailedError( "Failed to extract single pane: Caused by " + e.getMessage() )
+        }
+
+        File illegalDirectory = dist.testFile( "non-existant" ).createDir();
+        if( illegalDirectory.equals( singlePane.getCurrentDirectory() ) ) {
+          throw new AssertionFailedError( "Directory already set to 'test' directory. The test is not setup correctly." );
+        }
+
+        //this is required to get the ball rolling
+        singlePane.aboutToShow();
+
+        //set the current directory after calling aboutToShow (otherwise, it'll stomp over us when it restores its default settings)
+        singlePane.setCurrentDirectory( illegalDirectory );
+
+        //close the UI. This saves the current settings.
+        singlePane.close();
+
+        //now instantiate it again
+        testSingleDualPaneUIInteractionVersion1 = new TestSingleDualPaneUIInteractionVersion1( new TestAlternateUIInteractionVersion1(), settingsNode );
+        try {
+            singlePane = UIFactory.createSinglePaneUI(getClass().getClassLoader(), dist.getGradleHomeDir(), testSingleDualPaneUIInteractionVersion1, false );
+        } catch (Exception e) {
+            throw new AssertionFailedError( "Failed to extract single pane (second time): Caused by " + e.getMessage() )
+        }
+
+        //this should restore the previous settings
+        singlePane.aboutToShow();
+
+        Assert.assertEquals( illegalDirectory, singlePane.getCurrentDirectory() );
+    }
+
+    /**
+     * This tests that the command line altering mechanism works. This adds additional
+     * things to the command line being executed. This is used by gradle build systems
+     * that need custom, system-specific arguments passed to it that aren't known by gradle
+     * proper. For example: you're working with a large project made of MANY subprojects, a
+     * custom IDE plugin can track which projects you're focusing on and pass that information
+     * to the build system via this mechanism, so it only builds appropriate projects.
+     * This is awkward to test because its real use requires a customized build system,
+     * so I'm going to pass an argument that is illegal by itself -- meaning no tasks
+     * are specified. Then I'll alter the command line by adding an actual task. Then
+     * wait for it to complete and verify what was executed
+    */
+    @Test
+    public void testCommandLineAlteringListener()
+    {
+      DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
+
+      //this starts the execution queue. This also initiates a refresh that we'll ignore later.
+      dualPane.aboutToShow()
+
+      //add a request observer so we can observe when the command is finished. This allows us to
+      //see what was actually executed.
+      BlockingRequestObserver testRequestObserver = new BlockingRequestObserver( RequestVersion1.EXECUTION_TYPE )
+      gradleInterface.addRequestObserver( testRequestObserver )
+
+      //now that we know that command is illegal by itself, try it again but the listener will append 'build'
+      //to the command line which makes it legal (again, we don't really care what we execute.
+      TestCommandLineArgumentAlteringListenerVersion1 commandLineArgumentAlteringListener = new TestCommandLineArgumentAlteringListenerVersion1("classes")
+      gradleInterface.addCommandLineArgumentAlteringListener( commandLineArgumentAlteringListener )
+
+      //execute this before we do our test. This is not legal by itself. It should fail. That means our
+      //test is setup correctly. For example: if someone adds a default task to this project, this will
+      //generate NO error and thus, our test will prove nothing. If you get a test failure here, you
+      //can try changing the command line to something that's illegal by itself (we don't care what).
+      RequestVersion1 request = gradleInterface.executeCommand2("-s", "test command")
+
+      testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+      Assert.assertThat( testRequestObserver.request.getFullCommandLine(), startsWith( "-s " ) )
+      Assert.assertThat( testRequestObserver.request.getFullCommandLine(), endsWith( " classes" ) )
+
+      //make sure it completed execution correctly
+      Assert.assertEquals( "Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output , 0, testRequestObserver.result )
+
+      //the request that was executed should be equal to our original command with our 'altered' command added to it
+      Assert.assertNotNull( "Missing 'execution completed' request", testRequestObserver.request )
+
+      //just to be paranoid, let's make sure it was actually our request. If this fails, it probably represents something
+      //fundamentally flawed with the request or request wrapper mechanism.
+      Assert.assertEquals( request, testRequestObserver.request )
+
+      gradleInterface.removeRequestObserver( testRequestObserver )
+      gradleInterface.removeCommandLineArgumentAlteringListener( commandLineArgumentAlteringListener )
+    }
+
+   /**
+    * This tests that getVersion returns the same thing as the jar's suffix.
+    * We'll get the gradle jar, then strip off its extension and verify that
+    * the jar's name ends with the version number.
+    */
+  @Test
+  public void testVersion()
+  {
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+    String version = ( (GradleInterfaceVersion2) singlePane.getGradleInterfaceVersion1()).getVersion()
+
+    Assert.assertNotNull( "null version number", version )
+
+    Assert.assertFalse( "Empty version number", version.trim().equals( "" ) )       //shouldn't be empty
+
+    File gradleJar = ExternalUtility.getGradleJar(dist.gradleHomeDir)
+
+    Assert.assertNotNull( "Missing gradle jar", gradleJar )                         //we should have a gradle jar
+
+    int indexOfExtension = gradleJar.getName().toLowerCase().lastIndexOf( ".jar" )  //get the index of its extension
+
+    Assert.assertTrue( "Has no '.jar' extension", indexOfExtension != -1 )          //it had better have an extension
+
+    String name = gradleJar.getName().substring( 0, indexOfExtension )              //get its name minus the extension
+
+    Assert.assertTrue( "Jar name doesn't end with version", name.endsWith( version ) )  //the name (minus extension) should end with the version
+  }
+
+  /**
+   * This is just a spot check that getGradleHomeDirectory works. Its based off
+   * of the gradle you're running.
+   */
+  @Test
+  public void testGradleHomeDirectory()
+  {
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+
+    Assert.assertEquals( dist.gradleHomeDir, singlePane.getGradleHomeDirectory() )
+  }
+
+
+  /**
+   * This is just a spot check that we can get an instance of the OutputUILord.
+   * Other tests cover its functionality more thoroughly. This is just to make sure
+   * its working when accessed via the Open API.
+   */
+  @Test
+  public void testOutputUILord()
+  {
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+    Assert.assertNotNull( outputUILord )
+  }
+
+  /**
+   * This tests that you can correctly obtain the number of output tabs from a
+   * dual pane UI. This
+   */
+  @Test
+  public void testDualPaneOutputPaneNumber()
+  {
+    if ( java.awt.GraphicsEnvironment.isHeadless() ) {
+      return;  // Can't run this test in headless mode!
+    }
+
+    DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+
+    //now create a frame, place the UI in it, then show it briefly
+    JFrame frame = openApi.open(dualPane)
+
+    //make sure we got something
+    Assert.assertNotNull( dualPane )
+
+    //tell it we're about to show it, so it'll create a component
+    dualPane.aboutToShow()
+
+    dualPane.refreshTaskTree()
+
+    openApi.flushEventQueue( frame )
+
+    //there should be one opened output tab for the refresh
+    Assert.assertEquals( 1, dualPane.getNumberOfOpenedOutputTabs() )
+
+    dualPane.executeCommand( "build", "build" )
+
+    openApi.flushEventQueue( frame )
+
+    //there should be 2 opened output tabs. One for refresh, one for build
+    Assert.assertEquals( 2, dualPane.getNumberOfOpenedOutputTabs() )
+  }
+
+   /**
+  * This tests whether or not a the UI is considered busy. Its busy if its
+    * executing a command. To test this, we'll execute a command and verify
+    * we're busy. When it finishes, we'll verify we're not longer busy.
+    * We'll also check that canClose works properly. If we're busy, calling
+    * canClose should prompt the user to confirm closing and return their
+    * answer. If we're not busy, it should not prompt the user and return
+    * true.
+   */
+  @Test
+  public void testBusy()
+  {
+      DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
+
+      //this starts the execution queue. This also initiates a refresh that we'll ignore later.
+      dualPane.aboutToShow()
+
+      //add a request observer so we can observe when the command is finished.
+      BlockingRequestObserver testRequestObserver = new BlockingRequestObserver( RequestVersion1.EXECUTION_TYPE )
+      gradleInterface.addRequestObserver( testRequestObserver )
+
+      gradleInterface.executeCommand("build", "test command")
+
+      //now that there's a real command in the queue, we should be considered busy
+      Assert.assertTrue( dualPane.isBusy() )
+      Assert.assertTrue( gradleInterface.isBusy() )
+
+      //we're busy, we shouldn't be able to close
+      TestCloseInteraction testCloseInteraction = new TestCloseInteraction( false )
+      Assert.assertFalse( dualPane.canClose( testCloseInteraction ) )
+
+      //since we just asked to close and we're busy, make sure we prompted the user
+      Assert.assertTrue( testCloseInteraction.wasPromptedToConfirmClose )
+
+      testRequestObserver.waitForRequestExecutionComplete(120, TimeUnit.SECONDS)
+
+      Assert.assertThat( testRequestObserver.request.getFullCommandLine(), startsWith( "build" ) )
+
+      //make sure it completed execution correctly
+      Assert.assertEquals( "Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output , 0, testRequestObserver.result )
+
+      //make sure we're not longer considered busy
+      Assert.assertFalse( dualPane.isBusy() )
+      Assert.assertFalse( gradleInterface.isBusy() )
+
+      //make sure we can close now
+      testCloseInteraction = new TestCloseInteraction( false )
+      Assert.assertTrue( dualPane.canClose( testCloseInteraction ) )
+
+      //since we just asked to close and we're NOT busy, make sure we did NOT prompt the user
+      Assert.assertFalse( testCloseInteraction.wasPromptedToConfirmClose )
+
+      gradleInterface.removeRequestObserver( testRequestObserver )
+  }
+
+   /**
+   * This tests that we can set a custom gradle executor.
+    */
+    @Test
+    public void testSettingCustomGradleExecutor()
+    {
+       DualPaneUIVersion1 dualPane = openApi.createDualPaneUI()
+      GradleInterfaceVersion2 gradleInterface = (GradleInterfaceVersion2) dualPane.getGradleInterfaceVersion1()
+
+      //it should be null by default
+      Assert.assertNull( gradleInterface.getCustomGradleExecutable() )
+
+      //now let's set it to a custom gradle executable. Actually, we're not going to really get
+      //a custom one; we'll use the normal one. Why? Because a real custom one would probably
+      //become a pain for maintaining this test. Here, we're interested that the basics are working
+      //from an open-api standpoint.
+      File gradleExecutor = getCustomGradleExecutable()
+
+      gradleInterface.setCustomGradleExecutable( gradleExecutor )
+
+      //make sure it was set
+      Assert.assertEquals( gradleExecutor, gradleInterface.getCustomGradleExecutable() )
+      Assert.assertEquals( gradleExecutor, dualPane.getCustomGradleExecutable() ) //just another way to get it
+
+      //add a request observer so we can observe when the command is finished.
+      BlockingRequestObserver testRequestObserver = new BlockingRequestObserver( RequestVersion1.REFRESH_TYPE )
+      gradleInterface.addRequestObserver( testRequestObserver )
+
+      //this starts the execution queue
+      dualPane.aboutToShow()
+
+      dualPane.refreshTaskTree()
+
+      testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+      //make sure it completed execution correctly
+      Assert.assertEquals( "Execution failed with return code: " + testRequestObserver.result + "\nOutput:\n" + testRequestObserver.output , 0, testRequestObserver.result )
+
+      gradleInterface.removeRequestObserver( testRequestObserver )
+    }
+
+   /**
+    * This gets a gradle executable. That is, a way to launch gradle (shell script or batch file).
+    */
+    private File getCustomGradleExecutable()
+    {
+      //now let's set it to a custom gradle executable. We'll just point it to the regular
+      //gradle file (but it'll be the custom one.
+      String name = OperatingSystem.current().getScriptName("bin/gradle");
+
+      File gradleExecutor = new File( dist.getGradleHomeDir(), name )
+
+      //make sure the executable exists
+      Assert.assertTrue( "Missing gradle executable at: " + gradleExecutor, gradleExecutor.exists() )
+
+      return gradleExecutor
+    }
+}
+
+  /**
+   * Inner class for tracking a component's visiblity has changed.
+   * A HierarchyListener is how Swing notifies you that a component's visibility has changed.
+   * We'll use it to track if the component was shown and then hidden.
+   */
+    private class TestVisibilityHierarchyListener implements HierarchyListener
+    {
+      private boolean componentWasShown = false;
+      private boolean componentWasHidden = false;
+
+       public void hierarchyChanged(HierarchyEvent e)
+       {
+         if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED)!=0)
+         {
+            if( e.getComponent().isShowing() ) {
+              componentWasShown = true;
+            }
+            else {
+              componentWasHidden = true;
+            }
+         }
+       }
+     }
+
+ /**
+ * A class that manages a dummy gradle tab. It just consists of a label,
+ *  but tracks that certain fields were called.
+  */
+  public class TestTab implements GradleTabVersion1
+  {
+    private JLabel label = new JLabel("Testing Testing 123")
+    private TestVisibilityHierarchyListener hierarchyAdapter = new TestVisibilityHierarchyListener()
+    private boolean nameRetrieved
+    private boolean informedAboutToShow
+    private boolean componentCreated
+
+    def TestTab() {
+      label.addHierarchyListener( hierarchyAdapter )
+    }
+
+    private void reset() {
+      label.removeHierarchyListener( hierarchyAdapter )         //remove the existing listener
+      hierarchyAdapter = new TestVisibilityHierarchyListener()  //create a new one
+      label.addHierarchyListener( hierarchyAdapter )            //add it
+
+      nameRetrieved = false
+      informedAboutToShow = false
+      componentCreated = false
+    }
+
+    String getName() {
+      nameRetrieved = true;
+      return "Test Tab";
+    }
+
+    Component createComponent() {
+      componentCreated = true;
+      return label;
+    }
+
+    void aboutToShow() {
+      informedAboutToShow = true;
+    }
+  }
+
+  /**
+   * Class that tracks whether we were prompted to confirm close. It also returns a specific
+   * value to that prompt.
+   */
+    private class TestCloseInteraction implements BasicGradleUIVersion1.CloseInteraction
+    {
+      boolean wasPromptedToConfirmClose
+      boolean promptResult
+
+
+
+      def TestCloseInteraction(promptResult) {
+        this.promptResult = promptResult;
+      }
+
+      boolean promptUserToConfirmClosingWhileBusy() {
+        wasPromptedToConfirmClose = true
+        return promptResult
+      }
+    }
+
+  /**
+   * This appends a specified string to the command line when executing a command. 
+   */
+  private class TestCommandLineArgumentAlteringListenerVersion1 implements CommandLineArgumentAlteringListenerVersion1
+  {
+    private final String additionalArguments;
+
+    def TestCommandLineArgumentAlteringListenerVersion1(additionalArguments) {
+      this.additionalArguments = additionalArguments;
+    }
+
+    String getAdditionalCommandLineArguments(String commandLineArguments) {
+      if( commandLineArguments.startsWith( "-s " ) ) {  //we're only interested in altering this one command
+        return additionalArguments;
+      }
+
+      return null;
+    }
+  }
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OutputUILordTest.groovy b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OutputUILordTest.groovy
new file mode 100644
index 0000000..8e62a9a
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/OutputUILordTest.groovy
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+
+import java.awt.Font
+import java.util.concurrent.TimeUnit
+import javax.swing.UIManager
+import org.gradle.integtests.DistributionIntegrationTestRunner
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.TestResources
+import org.gradle.openapi.external.ui.OutputUILordVersion1
+import org.gradle.openapi.external.ui.SinglePaneUIVersion1
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import static org.hamcrest.Matchers.startsWith
+
+/**
+ * Tests aspects of the OutputUILord in OpenAPI
+ *
+ * @author mhunsicker
+ */
+ at RunWith(DistributionIntegrationTestRunner.class)
+public class OutputUILordTest  {
+  @Rule public final GradleDistribution dist = new GradleDistribution()
+  @Rule public final OpenApiFixture openApi = new OpenApiFixture()
+  @Rule public final TestResources resources = new TestResources('testProject')
+
+  /**
+  * This verifies that you can add file extension to the output lord. This is for
+  * highlighting file links in the output. Here, we're just interested in whether
+  * or not the functions work via/exists in the Open API. The actual functionality
+  * is tested elsewhere.
+  */
+  @Test
+  public void testAddingFileExtension()
+  {
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+
+    outputUILord.addFileExtension( '.txt', ':' )
+    List extensions = outputUILord.getFileExtensions()
+    Assert.assertTrue( extensions.contains( '.txt' ) )
+  }
+
+  /**
+  * This verifies that you can add prefixed file extensions to the output lord. This
+  * is for highlighting file links in the output. Here, we're just interested in whether
+  * or not the functions work via/exists in the Open API. The actual functionality is tested elsewhere.
+  */
+  @Test
+  public void testAddingPrefixedFileLink()
+  {
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+
+    outputUILord.addPrefixedFileLink( "Error Text", "The error is:", ".txt", ":" )
+  }
+
+  /**
+  * This tests setting the font. There's not much here to do other than set it and then
+  * get it, making sure its the same. This isn't worried so much about the font itself as
+  * much as the open API doesn't have a problem with setting the font.
+  */
+  @Test
+  public void testFont()
+  {
+    if ( java.awt.GraphicsEnvironment.isHeadless() ) {
+       return;  // Can't run this test in headless mode!
+    }
+
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+    Font font = UIManager.getFont( "Button.font" )  //this specific font is not important
+
+    //make sure that the above font doesn't happen to be the default font for the output lord. If it
+    //is, this test will silently succeed even if it should fail.
+    Assert.assertNotSame( "Fonts are the same. This test is not setup correctly.", font, outputUILord.getOutputTextFont() )
+
+    //now set the new font and then make sure it worked
+    outputUILord.setOutputTextFont( font )
+  }
+
+  /**
+  *
+  */
+  @Test
+  public void testReExecute()
+  {
+    SinglePaneUIVersion1 singlePane = openApi.createSinglePaneUI()
+    OutputUILordVersion1 outputUILord = singlePane.getOutputLord()
+
+    //this starts the execution queue. This also initiates a refresh that we'll ignore later.
+    singlePane.aboutToShow()
+
+    BlockingRequestObserver testRequestObserver = new BlockingRequestObserver()
+    singlePane.getGradleInterfaceVersion1().addRequestObserver( testRequestObserver )
+
+    //now execute a command
+    singlePane.executeCommand( "build", "test build")
+
+    //wait for it to complete
+    testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+    testRequestObserver.reset()
+
+    //now the single command we're trying to test
+    outputUILord.reExecuteLastCommand();
+
+    //wait again for it exit
+    testRequestObserver.waitForRequestExecutionComplete(80, TimeUnit.SECONDS)
+
+    //make sure it executed the correct request
+    Assert.assertThat( testRequestObserver.request.getFullCommandLine(), startsWith('build') )
+  }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestAlternateUIInteractionVersion1.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestAlternateUIInteractionVersion1.java
new file mode 100644
index 0000000..f12e229
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestAlternateUIInteractionVersion1.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
+
+import java.io.File;
+
+/**
+ * Implementation of AlternateUIInteractionVersion1 for testing purposes.
+ * This would lend itself well for mocking. 
+ *
+ * @author mhunsicker
+  */
+public class TestAlternateUIInteractionVersion1 implements AlternateUIInteractionVersion1 {
+
+    private boolean supportsEditingOpeningFiles;
+
+    public TestAlternateUIInteractionVersion1() {
+    }
+
+    public TestAlternateUIInteractionVersion1(boolean supportsEditingOpeningFiles) {
+        this.supportsEditingOpeningFiles = supportsEditingOpeningFiles;
+    }
+
+    public void openFile(File file, int line) {
+
+    }
+
+    public void editFile(File file, int line) {
+
+    }
+
+    public boolean doesSupportEditingOpeningFiles() {
+        return supportsEditingOpeningFiles;
+    }
+
+    public void aboutToExecuteCommand(String fullCommandLine) {
+
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSettingsNodeVersion1.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSettingsNodeVersion1.java
new file mode 100644
index 0000000..95fb63d
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSettingsNodeVersion1.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+import org.gradle.openapi.external.ui.SettingsNodeVersion1;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Implementation of settings node. It basically mirrors a DOM.
+ * @author mhunsicker
+ */
+public class TestSettingsNodeVersion1 implements SettingsNodeVersion1 {
+
+    private String name;
+    private String value;
+    private HashMap<String,String> attributes = new HashMap<String, String>();
+    private SettingsNodeVersion1 parent;
+    private List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
+
+
+    public TestSettingsNodeVersion1() {
+        //this creates a root settings node.
+    }
+
+    public TestSettingsNodeVersion1(SettingsNodeVersion1 parent) {
+        this.parent = parent;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValueOfChild(String name, String value) {
+        SettingsNodeVersion1 settingsNode = addChildIfNotPresent(name);
+        settingsNode.setValue(value);
+    }
+
+    public String getValueOfChild(String name, String defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+            if (value != null) {
+                return value;
+            }
+        }
+        return defaultValue;
+    }
+
+    public SettingsNodeVersion1 getChildNode(String name) {
+        Iterator<SettingsNodeVersion1> iterator = children.iterator();
+        while (iterator.hasNext()) {
+            SettingsNodeVersion1 childNode = iterator.next();
+            if (name.equals(childNode.getName() )) {
+                return childNode;
+            }
+        }
+        return null;
+    }
+
+    public List<SettingsNodeVersion1> getChildNodes() {
+        return children;
+    }
+
+    public List<SettingsNodeVersion1> getChildNodes(String name) {
+        List<SettingsNodeVersion1> children = new ArrayList<SettingsNodeVersion1>();
+
+        Iterator<SettingsNodeVersion1> iterator = children.iterator();
+        while (iterator.hasNext()) {
+            SettingsNodeVersion1 childNode = iterator.next();
+            if (name.equals(childNode.getName()) ) {
+                children.add(childNode);
+            }
+        }
+
+        return children;
+    }
+
+    public int getValueOfChildAsInt(String name, int defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+
+            try {
+                if (value != null) {
+                    return Integer.parseInt(value);
+                }
+            } catch (NumberFormatException e) {
+                //we couldn't parse it. Just return the default.
+            }
+        }
+        return defaultValue;
+    }
+
+    public void setValueOfChildAsInt(String name, int value) {
+        setValueOfChild(name, Integer.toString(value));
+    }
+
+    public long getValueOfChildAsLong(String name, long defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+
+            try {
+                if (value != null) {
+                    return Long.parseLong(value);
+                }
+            } catch (NumberFormatException e) {
+                //we couldn't parse it. Just return the default.
+            }
+        }
+        return defaultValue;
+    }
+
+    public void setValueOfChildAsLong(String name, long value) {
+        setValueOfChild(name, Long.toString(value));
+    }
+
+    public boolean getValueOfChildAsBoolean(String name, boolean defaultValue) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode != null) {
+            String value = settingsNode.getValue();
+
+            //I'm not calling 'Boolean.parseBoolean( value )' because it will return false if the value isn't true/false
+            //and we want it to return whatever the default is if its not a boolean.
+            if (value != null) {
+                if ("true".equalsIgnoreCase(value)) {
+                    return true;
+                }
+
+                if ("false".equalsIgnoreCase(value)) {
+                    return false;
+                }
+            }
+        }
+
+        return defaultValue;
+    }
+
+    public void setValueOfChildAsBoolean(String name, boolean value) {
+        setValueOfChild(name, Boolean.toString(value));
+    }
+
+    public SettingsNodeVersion1 addChild(String name) {
+        SettingsNodeVersion1 childNode = new TestSettingsNodeVersion1( this );
+        childNode.setName( name );
+
+        children.add( childNode );
+        return childNode;
+    }
+
+    public SettingsNodeVersion1 addChildIfNotPresent(String name) {
+        SettingsNodeVersion1 settingsNode = getChildNode(name);
+        if (settingsNode == null) {
+            settingsNode = addChild(name);
+        }
+
+        return settingsNode;
+    }
+
+    public SettingsNodeVersion1 getNodeAtPath(String... pathPortions) {
+        if (pathPortions == null || pathPortions.length == 0) {
+            return null;
+        }
+
+        String firstPathPortion = pathPortions[0];
+
+        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
+
+        int index = 1; //Skip the first one. we've already used that one.
+        while (index < pathPortions.length && currentNode != null) {
+            String pathPortion = pathPortions[index];
+            currentNode = currentNode.getChildNode(pathPortion);
+            index++;
+        }
+
+        return currentNode;
+    }
+
+    private SettingsNodeVersion1 getNodeAtPathCreateIfNotFound(String... pathPortions) {
+        if (pathPortions == null || pathPortions.length == 0) {
+            return null;
+        }
+
+        String firstPathPortion = pathPortions[0];
+
+        SettingsNodeVersion1 currentNode = getChildNode(firstPathPortion);
+        if (currentNode == null) {
+            currentNode = addChild(firstPathPortion);
+        }
+
+        int index = 1;
+        while (index < pathPortions.length) {
+            String pathPortion = pathPortions[index];
+            currentNode = currentNode.getChildNode(pathPortion);
+            if (currentNode == null) {
+                currentNode = addChild(firstPathPortion);
+            }
+
+            index++;
+        }
+
+        return currentNode;
+    }
+
+    public void removeFromParent() {
+        ((TestSettingsNodeVersion1) this.parent).children.remove( this );
+        this.parent = null;
+    }
+
+    public void removeAllChildren() {
+        children.clear();
+    }
+
+    @Override
+    public String toString() {
+        return getName() + "='" + getValue() + "' " + children.size() + " children";
+    }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSingleDualPaneUIInteractionVersion1.java b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSingleDualPaneUIInteractionVersion1.java
new file mode 100644
index 0000000..8871c29
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/groovy/org/gradle/integtests/openapi/TestSingleDualPaneUIInteractionVersion1.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests.openapi;
+
+import org.gradle.openapi.external.ui.AlternateUIInteractionVersion1;
+import org.gradle.openapi.external.ui.DualPaneUIInteractionVersion1;
+import org.gradle.openapi.external.ui.SettingsNodeVersion1;
+import org.gradle.openapi.external.ui.SinglePaneUIInteractionVersion1;
+
+/**
+ * This is a test implementation of both SinglePaneUIInteractionVersion1 and DualPaneUIInteractionVersion1.
+ * This is really just a container to hand off more complex interactions to the UI when asked for.
+ * @author mhunsicker
+ */
+public class TestSingleDualPaneUIInteractionVersion1 implements SinglePaneUIInteractionVersion1, DualPaneUIInteractionVersion1 {
+
+    private AlternateUIInteractionVersion1 alternateUIInteractionVersion1;
+    private SettingsNodeVersion1 settingsNodeVersion1;
+
+    public TestSingleDualPaneUIInteractionVersion1(AlternateUIInteractionVersion1 alternateUIInteractionVersion1, SettingsNodeVersion1 settingsNodeVersion1) {
+        this.alternateUIInteractionVersion1 = alternateUIInteractionVersion1;
+        this.settingsNodeVersion1 = settingsNodeVersion1;
+    }
+
+    public AlternateUIInteractionVersion1 instantiateAlternateUIInteraction() {
+        return alternateUIInteractionVersion1;
+    }
+
+    public SettingsNodeVersion1 instantiateSettings() {
+        return settingsNodeVersion1;
+    }
+}
diff --git a/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/CompatibilityIntegrationTest/shared/build.gradle b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/CompatibilityIntegrationTest/shared/build.gradle
new file mode 100644
index 0000000..20dc96c
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/CompatibilityIntegrationTest/shared/build.gradle
@@ -0,0 +1,3 @@
+allprojects {
+    usePlugin 'java'
+}
diff --git a/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/CompatibilityIntegrationTest/shared/settings.gradle b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/CompatibilityIntegrationTest/shared/settings.gradle
new file mode 100644
index 0000000..955919e
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/CompatibilityIntegrationTest/shared/settings.gradle
@@ -0,0 +1 @@
+include 'a', 'b', 'c'
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/build.gradle b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/build.gradle
new file mode 100644
index 0000000..09c8011
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/build.gradle
@@ -0,0 +1 @@
+allprojects { apply plugin: 'java' }
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/settings.gradle b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/settings.gradle
new file mode 100644
index 0000000..56b61cd
--- /dev/null
+++ b/subprojects/gradle-open-api/src/integTest/resources/org/gradle/integtests/openapi/testproject/settings.gradle
@@ -0,0 +1 @@
+include 'services:webservice', 'api', 'shared'
\ No newline at end of file
diff --git a/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/runner/GradleRunnerInteractionVersion1.java b/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/runner/GradleRunnerInteractionVersion1.java
index 1e3efb4..3609844 100644
--- a/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/runner/GradleRunnerInteractionVersion1.java
+++ b/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/runner/GradleRunnerInteractionVersion1.java
@@ -32,8 +32,8 @@ public interface GradleRunnerInteractionVersion1
    */
    public File getWorkingDirectory();
 
-   public enum LogLevel { Quiet, Lifecycle, Debug };
-   public enum StackTraceLevel { InternalExceptions, Always, AlwaysFull };
+   public enum LogLevel { Quiet, Lifecycle, Debug }
+   public enum StackTraceLevel { InternalExceptions, Always, AlwaysFull }
 
    /*
       @return the log level. This determines the detail level of information
diff --git a/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/ui/GradleUIInteractionVersion1.java b/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/ui/GradleUIInteractionVersion1.java
index b19173e..3408ba4 100644
--- a/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/ui/GradleUIInteractionVersion1.java
+++ b/subprojects/gradle-open-api/src/main/groovy/org/gradle/openapi/external/ui/GradleUIInteractionVersion1.java
@@ -27,14 +27,14 @@ package org.gradle.openapi.external.ui;
 public interface GradleUIInteractionVersion1
 {
    /*
-      This is only called once and is how we get ahold of the AlternateUIInteraction.
+      This is only called once and is how we get a hold of the AlternateUIInteraction.
       @return an AlternateUIInteraction object. This cannot be null.
       @author mhunsicker
    */
    public AlternateUIInteractionVersion1 instantiateAlternateUIInteraction();
 
    /*
-      This is only called once and is how we get ahold of how the owner wants
+      This is only called once and is how we get a hold of how the owner wants
       to store preferences.
       @return a settings object. This cannot be null.
       @author mhunsicker
diff --git a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/AnalyzerFactory.java b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/AnalyzerFactory.java
deleted file mode 100644
index a08117d..0000000
--- a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/AnalyzerFactory.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.plugins.osgi;
-
-/**
- * @author Hans Dockter
- */
-public interface AnalyzerFactory {
-    ContainedVersionAnalyzer createAnalyzer();
-}
diff --git a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactory.java b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactory.java
index ea46e18..fb963c5 100644
--- a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactory.java
+++ b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactory.java
@@ -15,11 +15,13 @@
  */
 package org.gradle.api.internal.plugins.osgi;
 
+import org.gradle.api.internal.Factory;
+
 /**
  * @author Hans Dockter
  */
-public class DefaultAnalyzerFactory implements AnalyzerFactory {
-    public ContainedVersionAnalyzer createAnalyzer() {
+public class DefaultAnalyzerFactory implements Factory<ContainedVersionAnalyzer> {
+    public ContainedVersionAnalyzer create() {
         return new ContainedVersionAnalyzer();
     }
 }
diff --git a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java
index d2dfd1b..161fec4 100644
--- a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java
+++ b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifest.java
@@ -17,6 +17,7 @@ package org.gradle.api.internal.plugins.osgi;
 
 import aQute.lib.osgi.Analyzer;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.java.archives.internal.DefaultManifest;
 import org.gradle.api.plugins.osgi.OsgiManifest;
@@ -44,7 +45,7 @@ public class DefaultOsgiManifest extends DefaultManifest implements OsgiManifest
 
     private File classesDir;
 
-    private AnalyzerFactory analyzerFactory = new DefaultAnalyzerFactory();
+    private Factory<ContainedVersionAnalyzer> analyzerFactory = new DefaultAnalyzerFactory();
 
     private Map<String, List<String>> instructions = new HashMap<String, List<String>>();
 
@@ -56,7 +57,7 @@ public class DefaultOsgiManifest extends DefaultManifest implements OsgiManifest
 
     @Override
     public DefaultManifest getEffectiveManifest() {
-        ContainedVersionAnalyzer analyzer = analyzerFactory.createAnalyzer();
+        ContainedVersionAnalyzer analyzer = analyzerFactory.create();
         DefaultManifest effectiveManifest = new DefaultManifest(null);
         try {
             setAnalyzerProperties(analyzer);
@@ -196,11 +197,11 @@ public class DefaultOsgiManifest extends DefaultManifest implements OsgiManifest
         this.classpath = classpath;
     }
 
-    public AnalyzerFactory getAnalyzerFactory() {
+    public Factory<ContainedVersionAnalyzer> getAnalyzerFactory() {
         return analyzerFactory;
     }
 
-    public void setAnalyzerFactory(AnalyzerFactory analyzerFactory) {
+    public void setAnalyzerFactory(Factory<ContainedVersionAnalyzer> analyzerFactory) {
         this.analyzerFactory = analyzerFactory;
     }
 }
diff --git a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java
index 70c5098..9c20193 100644
--- a/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java
+++ b/subprojects/gradle-osgi/src/main/groovy/org/gradle/api/plugins/osgi/OsgiManifest.java
@@ -22,6 +22,8 @@ import java.util.List;
 import java.util.Map;
 
 /**
+ * Represents a manifest file for a JAR containing an OSGi bundle.
+ * 
  * @author Hans Dockter
  */
 public interface OsgiManifest extends org.gradle.api.java.archives.Manifest {
@@ -78,7 +80,7 @@ public interface OsgiManifest extends org.gradle.api.java.archives.Manifest {
     void setSymbolicName(String symbolicName);
 
     /**
-     * Returns the name
+     * Returns the name.
      *
      * @see #setName(String)
      */
@@ -92,7 +94,7 @@ public interface OsgiManifest extends org.gradle.api.java.archives.Manifest {
     void setName(String name);
 
     /**
-     * Returns the version
+     * Returns the version.
      *
      * @see #setVersion(String) 
      */
@@ -120,7 +122,7 @@ public interface OsgiManifest extends org.gradle.api.java.archives.Manifest {
     void setDescription(String description);
 
     /**
-     * Returns the license
+     * Returns the license.
      * @see #setLicense(String) 
      */
     String getLicense();
diff --git a/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactoryTest.java b/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactoryTest.java
index fe1054b..6b51460 100644
--- a/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactoryTest.java
+++ b/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultAnalyzerFactoryTest.java
@@ -23,7 +23,7 @@ import org.junit.Test;import static org.junit.Assert.assertNotNull;
 public class DefaultAnalyzerFactoryTest {
     @Test
     public void create() {
-        ContainedVersionAnalyzer analyzer = new DefaultAnalyzerFactory().createAnalyzer();
+        ContainedVersionAnalyzer analyzer = new DefaultAnalyzerFactory().create();
         assertNotNull(analyzer);
     }
 }
diff --git a/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java b/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java
index 85e03d1..8cc36ed 100644
--- a/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java
+++ b/subprojects/gradle-osgi/src/test/groovy/org/gradle/api/internal/plugins/osgi/DefaultOsgiManifestTest.java
@@ -17,6 +17,7 @@ package org.gradle.api.internal.plugins.osgi;
 
 import aQute.lib.osgi.Analyzer;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.java.archives.internal.DefaultManifest;
 import org.gradle.util.GUtil;
@@ -42,7 +43,7 @@ import static org.junit.Assert.*;
 @RunWith(JMock.class)
 public class DefaultOsgiManifestTest {
     private DefaultOsgiManifest osgiManifest;
-    private AnalyzerFactory analyzerFactoryMock;
+    private Factory<ContainedVersionAnalyzer> analyzerFactoryMock;
     private ContainedVersionAnalyzer analyzerMock;
 
     private JUnit4Mockery context = new JUnit4Mockery() {{
@@ -53,10 +54,10 @@ public class DefaultOsgiManifestTest {
     @Before
     public void setUp() {
         osgiManifest = new DefaultOsgiManifest(fileResolver);
-        analyzerFactoryMock = context.mock(AnalyzerFactory.class);
+        analyzerFactoryMock = context.mock(Factory.class);
         analyzerMock = context.mock(ContainedVersionAnalyzer.class);
         context.checking(new Expectations() {{
-            allowing(analyzerFactoryMock).createAnalyzer();
+            allowing(analyzerFactoryMock).create();
             will(returnValue(analyzerMock));
         }});
         osgiManifest.setAnalyzerFactory(analyzerFactoryMock);
diff --git a/subprojects/gradle-plugins/plugins.gradle b/subprojects/gradle-plugins/plugins.gradle
index 673ca04..c8dda61 100644
--- a/subprojects/gradle-plugins/plugins.gradle
+++ b/subprojects/gradle-plugins/plugins.gradle
@@ -31,6 +31,10 @@ dependencies {
             libraries.ant,
             'org.testng:testng:5.12.1'
 
+    // This is for the ant junit task. Without it, we get file locking problems with some jvms because the default
+    // transformer does not close files
+    runtime 'xalan:xalan:2.7.1'
+
     testCompile libraries.xmlunit
 
     testCompile project(path: ':core', configuration: 'testFixtures')
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/plugins/ProcessResources.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/plugins/ProcessResources.java
new file mode 100644
index 0000000..3cb258e
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/plugins/ProcessResources.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.plugins;
+
+import org.gradle.api.internal.tasks.compile.SimpleStaleClassCleaner;
+import org.gradle.api.internal.tasks.compile.StaleClassCleaner;
+import org.gradle.api.tasks.Copy;
+
+public class ProcessResources extends Copy {
+    @Override
+    protected void copy() {
+        StaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
+        cleaner.setDestinationDir(getDestinationDir());
+        cleaner.execute();
+        super.copy();
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDepend.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDepend.java
new file mode 100644
index 0000000..ee6ef6d
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDepend.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.api.internal.tasks.compile;
+
+import org.apache.tools.ant.taskdefs.optional.depend.Depend;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.BuildException;
+
+public class AntDepend extends Depend {
+    private Path src;
+
+    public Path createSrc() {
+        if (src == null) {
+            src = new Path(getProject());
+        }
+        return src.createPath();
+    }
+
+    @Override
+    public void execute() throws BuildException {
+        setSrcdir(src);
+        super.execute();
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy
index 4e9c84c..f3b2799 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntDependsStaleClassCleaner.groovy
@@ -16,14 +16,13 @@
 package org.gradle.api.internal.tasks.compile
 
 import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.project.AntBuilderFactory
-import org.gradle.api.tasks.compile.AntDepend
+import org.gradle.api.internal.Factory
 
 class AntDependsStaleClassCleaner extends StaleClassCleaner {
-    private final AntBuilderFactory antBuilderFactory
+    private final Factory<AntBuilder> antBuilderFactory
     File dependencyCacheDir
 
-    def AntDependsStaleClassCleaner(AntBuilderFactory antBuilderFactory) {
+    def AntDependsStaleClassCleaner(Factory<AntBuilder> antBuilderFactory) {
         this.antBuilderFactory = antBuilderFactory;
     }
 
@@ -37,7 +36,7 @@ class AntDependsStaleClassCleaner extends StaleClassCleaner {
             dependOptions['cache'] = dependencyCacheDir
         }
 
-        def ant = antBuilderFactory.createAntBuilder()
+        def ant = antBuilderFactory.create()
         ant.project.addTaskDefinition('gradleDepend', AntDepend.class)
         ant.gradleDepend(dependOptions) {
             source.addToAntBuilder(ant, 'src', FileCollection.AntType.MatchingTask)
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy
index 3096740..ecb09e0 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/AntJavaCompiler.groovy
@@ -18,12 +18,11 @@ package org.gradle.api.internal.tasks.compile
 
 import org.gradle.api.AntBuilder
 import org.gradle.api.file.FileCollection
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
 import org.gradle.api.tasks.WorkResult
 import org.gradle.api.tasks.compile.CompileOptions
-import org.gradle.api.internal.project.AntBuilderFactory
+import org.gradle.api.internal.Factory
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
 
 /**
  * @author Hans Dockter
@@ -37,9 +36,9 @@ class AntJavaCompiler implements JavaCompiler {
     String sourceCompatibility;
     String targetCompatibility;
     CompileOptions compileOptions = new CompileOptions()
-    final AntBuilderFactory antBuilderFactory
+    final Factory<AntBuilder> antBuilderFactory
 
-    def AntJavaCompiler(AntBuilderFactory antBuilderFactory) {
+    def AntJavaCompiler(Factory<AntBuilder> antBuilderFactory) {
         this.antBuilderFactory = antBuilderFactory
     }
 
@@ -48,7 +47,7 @@ class AntJavaCompiler implements JavaCompiler {
     }
 
     WorkResult execute() {
-        def ant = antBuilderFactory.createAntBuilder()
+        def ant = antBuilderFactory.create()
         
         createAntClassPath(ant, classpath)
         Map otherArgs = [
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java
index 7894cdf..5d44ef1 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/compile/IncrementalJavaCompiler.java
@@ -15,17 +15,18 @@
  */
 package org.gradle.api.internal.tasks.compile;
 
+import org.gradle.api.AntBuilder;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.TaskOutputsInternal;
-import org.gradle.api.internal.project.AntBuilderFactory;
 
 import java.io.File;
 
 public class IncrementalJavaCompiler extends IncrementalJavaSourceCompiler<JavaCompiler> implements JavaCompiler {
-    private final AntBuilderFactory antBuilderFactory;
+    private final Factory<? extends AntBuilder> antBuilderFactory;
     private final TaskOutputsInternal taskOutputs;
     private File dependencyCacheDir;
 
-    public IncrementalJavaCompiler(JavaCompiler compiler, AntBuilderFactory antBuilderFactory,
+    public IncrementalJavaCompiler(JavaCompiler compiler, Factory<? extends AntBuilder> antBuilderFactory,
                                     TaskOutputsInternal taskOutputs) {
         super(compiler);
         this.antBuilderFactory = antBuilderFactory;
@@ -39,7 +40,7 @@ public class IncrementalJavaCompiler extends IncrementalJavaSourceCompiler<JavaC
 
     protected StaleClassCleaner createCleaner() {
         if (getCompileOptions().isUseDepend()) {
-            AntDependsStaleClassCleaner cleaner = new AntDependsStaleClassCleaner(antBuilderFactory);
+            AntDependsStaleClassCleaner cleaner = new AntDependsStaleClassCleaner((Factory) antBuilderFactory);
             cleaner.setDependencyCacheDir(dependencyCacheDir);
             return cleaner;
         } else {
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestClassProcessorFactory.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestClassProcessorFactory.java
deleted file mode 100644
index 424d0ea..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/TestClassProcessorFactory.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.internal.tasks.testing;
-
-public interface TestClassProcessorFactory {
-    TestClassProcessor create();
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/ClassFileExtractionManager.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/ClassFileExtractionManager.java
index 804771c..8164089 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/ClassFileExtractionManager.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/ClassFileExtractionManager.java
@@ -17,8 +17,6 @@ package org.gradle.api.internal.tasks.testing.detection;
 
 import org.apache.commons.lang.text.StrBuilder;
 import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.indexing.JarFilePackageListener;
-import org.gradle.api.artifacts.indexing.JarFilePackageLister;
 import org.gradle.util.JarUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
index bd5f726..8fefed3 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
@@ -17,14 +17,18 @@
 package org.gradle.api.internal.tasks.testing.detection;
 
 import org.gradle.api.file.FileTree;
-import org.gradle.api.internal.tasks.testing.*;
+import org.gradle.api.internal.Factory;
+import org.gradle.api.internal.tasks.testing.TestClassProcessor;
+import org.gradle.api.internal.tasks.testing.TestFramework;
+import org.gradle.api.internal.tasks.testing.TestResultProcessor;
+import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
 import org.gradle.api.internal.tasks.testing.processors.MaxNParallelTestClassProcessor;
 import org.gradle.api.internal.tasks.testing.processors.RestartEveryNTestClassProcessor;
 import org.gradle.api.internal.tasks.testing.processors.TestMainAction;
 import org.gradle.api.internal.tasks.testing.worker.ForkingTestClassProcessor;
 import org.gradle.api.tasks.testing.Test;
 import org.gradle.messaging.actor.ActorFactory;
-import org.gradle.process.internal.WorkerProcessFactory;
+import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.util.TrueTimeProvider;
 
 /**
@@ -33,10 +37,10 @@ import org.gradle.util.TrueTimeProvider;
  * @author Tom Eyckmans
  */
 public class DefaultTestExecuter implements TestExecuter {
-    private final WorkerProcessFactory workerFactory;
+    private final Factory<? extends WorkerProcessBuilder> workerFactory;
     private final ActorFactory actorFactor;
 
-    public DefaultTestExecuter(WorkerProcessFactory workerFactory, ActorFactory actorFactor) {
+    public DefaultTestExecuter(Factory<? extends WorkerProcessBuilder> workerFactory, ActorFactory actorFactor) {
         this.workerFactory = workerFactory;
         this.actorFactor = actorFactor;
     }
@@ -44,13 +48,13 @@ public class DefaultTestExecuter implements TestExecuter {
     public void execute(final Test testTask, TestResultProcessor testResultProcessor) {
         final TestFramework testFramework = testTask.getTestFramework();
         final WorkerTestClassProcessorFactory testInstanceFactory = testFramework.getProcessorFactory();
-        final TestClassProcessorFactory forkingProcessorFactory = new TestClassProcessorFactory() {
+        final Factory<TestClassProcessor> forkingProcessorFactory = new Factory<TestClassProcessor>() {
             public TestClassProcessor create() {
                 return new ForkingTestClassProcessor(workerFactory, testInstanceFactory, testTask,
                         testTask.getClasspath(), testFramework.getWorkerConfigurationAction());
             }
         };
-        TestClassProcessorFactory reforkingProcessorFactory = new TestClassProcessorFactory() {
+        Factory<TestClassProcessor> reforkingProcessorFactory = new Factory<TestClassProcessor>() {
             public TestClassProcessor create() {
                 return new RestartEveryNTestClassProcessor(forkingProcessorFactory, testTask.getForkEvery());
             }
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/JarFilePackageListener.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/JarFilePackageListener.java
new file mode 100644
index 0000000..99e9dfa
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/JarFilePackageListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.detection;
+
+/**
+ * @author Tom Eyckmans
+ */
+public interface JarFilePackageListener {
+    void receivePackage(String packageName);
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/JarFilePackageLister.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/JarFilePackageLister.java
new file mode 100644
index 0000000..3bd4b96
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/detection/JarFilePackageLister.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.detection;
+
+import org.gradle.api.GradleException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JarFilePackageLister {
+    public void listJarPackages(File jarFile, JarFilePackageListener listener) {
+        if (jarFile == null) {
+            throw new IllegalArgumentException("jarFile is null!");
+        }
+
+        final String jarFileAbsolutePath = jarFile.getAbsolutePath();
+
+        if (!jarFile.exists()) {
+            throw new IllegalArgumentException("jarFile doesn't exists! (" + jarFileAbsolutePath + ")");
+        }
+        if (!jarFile.isFile()) {
+            throw new IllegalArgumentException("jarFile is not a file! (" + jarFileAbsolutePath + ")");
+        }
+        if (!jarFile.getName().endsWith(".jar")) {
+            throw new IllegalArgumentException("jarFile is not a jarFile! (" + jarFileAbsolutePath + ")");
+        }
+
+        try {
+            ZipFile zipFile = new ZipFile(jarFile);
+            try {
+                final Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
+
+                while (zipFileEntries.hasMoreElements()) {
+                    final ZipEntry zipFileEntry = zipFileEntries.nextElement();
+
+                    if (zipFileEntry.isDirectory()) {
+                        final String zipFileEntryName = zipFileEntry.getName();
+
+                        if (!zipFileEntryName.startsWith("META-INF")) {
+                            listener.receivePackage(zipFileEntryName);
+                        }
+                    }
+                }
+            } finally {
+                zipFile.close();
+            }
+        } catch (IOException e) {
+            throw new GradleException("failed to scan jar file for packages (" + jarFileAbsolutePath + ")", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/CaptureTestOutputTestResultProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/CaptureTestOutputTestResultProcessor.java
deleted file mode 100644
index 6879a66..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/CaptureTestOutputTestResultProcessor.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.tasks.testing.junit;
-
-import org.gradle.api.internal.tasks.testing.*;
-import org.gradle.api.logging.StandardOutputListener;
-import org.gradle.logging.StandardOutputRedirector;
-
-public class CaptureTestOutputTestResultProcessor implements TestResultProcessor {
-    private final TestResultProcessor processor;
-    private final StandardOutputRedirector outputRedirector;
-    private Object suite;
-
-    public CaptureTestOutputTestResultProcessor(TestResultProcessor processor, StandardOutputRedirector outputRedirector) {
-        this.processor = processor;
-        this.outputRedirector = outputRedirector;
-    }
-
-    public void started(final TestDescriptorInternal test, TestStartEvent event) {
-        processor.started(test, event);
-        if (suite != null) {
-            return;
-        }
-        suite = test.getId();
-        outputRedirector.redirectStandardOutputTo(new StandardOutputListener() {
-            public void onOutput(CharSequence output) {
-                processor.output(suite, new TestOutputEvent(TestOutputEvent.Destination.StdOut, output.toString()));
-            }
-        });
-        outputRedirector.redirectStandardErrorTo(new StandardOutputListener() {
-            public void onOutput(CharSequence output) {
-                processor.output(suite, new TestOutputEvent(TestOutputEvent.Destination.StdErr, output.toString()));
-            }
-        });
-        outputRedirector.start();
-    }
-
-    public void completed(Object testId, TestCompleteEvent event) {
-        if (testId.equals(suite)) {
-            try {
-                outputRedirector.stop();
-            } finally {
-                suite = null;
-            }
-        }
-        processor.completed(testId, event);
-    }
-
-    public void output(Object testId, TestOutputEvent event) {
-        processor.output(testId, event);
-    }
-
-    public void failure(Object testId, Throwable result) {
-        processor.failure(testId, result);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JULRedirector.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JULRedirector.java
index 969869b..59d4c5f 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JULRedirector.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JULRedirector.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.tasks.testing.junit;
 
 import org.gradle.logging.StandardOutputCapture;
-import org.gradle.logging.DefaultStandardOutputRedirector;
+import org.gradle.logging.internal.DefaultStandardOutputRedirector;
 
 import java.util.logging.ConsoleHandler;
 import java.util.logging.LogManager;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
index e43557a..8beff82 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassExecuter.java
@@ -60,23 +60,21 @@ public class JUnitTestClassExecuter {
         Class<?> testClass;
         try {
             testClass = Class.forName(testClassName, true, applicationClassLoader);
+
+            // Look for a static suite method
+            try {
+                Method suiteMethod = testClass.getMethod("suite", new Class[0]);
+                return (Test) suiteMethod.invoke(null);
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            } catch (InvocationTargetException e) {
+                return new BrokenTest(Description.createTestDescription(testClass, "suite"), e.getCause());
+            }
         } catch (Throwable e) {
             return new BrokenTest(
                     Description.createSuiteDescription(String.format("initializationError(%s)", testClassName)), e);
         }
 
-        // Look for a static suite method
-        try {
-            Method suiteMethod = testClass.getMethod("suite", new Class[0]);
-            return (Test) suiteMethod.invoke(null);
-        } catch (NoSuchMethodException e) {
-            // Ignore
-        } catch (InvocationTargetException e) {
-            return new BrokenTest(Description.createTestDescription(testClass, "suite"), e.getCause());
-        } catch (IllegalAccessException e) {
-            return new BrokenTest(Description.createTestDescription(testClass, "suite"), e);
-        }
-
         return new JUnit4TestAdapter(testClass);
     }
 
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
index 238a74e..28cfdf9 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessor.java
@@ -19,6 +19,7 @@ package org.gradle.api.internal.tasks.testing.junit;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
+import org.gradle.api.internal.tasks.testing.processors.CaptureTestOutputTestResultProcessor;
 import org.gradle.api.internal.tasks.testing.results.AttachParentTestResultProcessor;
 import org.gradle.listener.ListenerBroadcast;
 import org.gradle.logging.StandardOutputRedirector;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java
new file mode 100644
index 0000000..5059881
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.processors;
+
+import org.gradle.api.internal.tasks.testing.*;
+import org.gradle.api.logging.StandardOutputListener;
+import org.gradle.logging.StandardOutputRedirector;
+
+/**
+ * A {@link org.gradle.api.internal.tasks.testing.TestResultProcessor} which redirect stdout and stderr during the
+ * execution of a test suite.
+ */
+public class CaptureTestOutputTestResultProcessor implements TestResultProcessor {
+    private final TestResultProcessor processor;
+    private final StandardOutputRedirector outputRedirector;
+    private Object suite;
+
+    public CaptureTestOutputTestResultProcessor(TestResultProcessor processor, StandardOutputRedirector outputRedirector) {
+        this.processor = processor;
+        this.outputRedirector = outputRedirector;
+    }
+
+    public void started(final TestDescriptorInternal test, TestStartEvent event) {
+        processor.started(test, event);
+        if (suite != null) {
+            return;
+        }
+        suite = test.getId();
+        outputRedirector.redirectStandardOutputTo(new StandardOutputListener() {
+            public void onOutput(CharSequence output) {
+                processor.output(suite, new TestOutputEvent(TestOutputEvent.Destination.StdOut, output.toString()));
+            }
+        });
+        outputRedirector.redirectStandardErrorTo(new StandardOutputListener() {
+            public void onOutput(CharSequence output) {
+                processor.output(suite, new TestOutputEvent(TestOutputEvent.Destination.StdErr, output.toString()));
+            }
+        });
+        outputRedirector.start();
+    }
+
+    public void completed(Object testId, TestCompleteEvent event) {
+        if (testId.equals(suite)) {
+            try {
+                outputRedirector.stop();
+            } finally {
+                suite = null;
+            }
+        }
+        processor.completed(testId, event);
+    }
+
+    public void output(Object testId, TestOutputEvent event) {
+        processor.output(testId, event);
+    }
+
+    public void failure(Object testId, Throwable result) {
+        processor.failure(testId, result);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java
index 6696edf..4cd1f25 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessor.java
@@ -16,8 +16,8 @@
 
 package org.gradle.api.internal.tasks.testing.processors;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
-import org.gradle.api.internal.tasks.testing.TestClassProcessorFactory;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
 import org.gradle.messaging.actor.Actor;
@@ -35,7 +35,7 @@ import java.util.List;
  */
 public class MaxNParallelTestClassProcessor implements TestClassProcessor {
     private final int maxProcessors;
-    private final TestClassProcessorFactory factory;
+    private final Factory<? extends TestClassProcessor> factory;
     private final ActorFactory actorFactory;
     private TestResultProcessor resultProcessor;
     private int pos;
@@ -43,7 +43,7 @@ public class MaxNParallelTestClassProcessor implements TestClassProcessor {
     private List<Actor> actors = new ArrayList<Actor>();
     private Actor resultProcessorActor;
 
-    public MaxNParallelTestClassProcessor(int maxProcessors, TestClassProcessorFactory factory, ActorFactory actorFactory) {
+    public MaxNParallelTestClassProcessor(int maxProcessors, Factory<? extends TestClassProcessor> factory, ActorFactory actorFactory) {
         this.maxProcessors = maxProcessors;
         this.factory = factory;
         this.actorFactory = actorFactory;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java
index 879ff91..38df380 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessor.java
@@ -16,19 +16,19 @@
 
 package org.gradle.api.internal.tasks.testing.processors;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
-import org.gradle.api.internal.tasks.testing.TestClassProcessorFactory;
 
 public class RestartEveryNTestClassProcessor implements TestClassProcessor {
-    private final TestClassProcessorFactory factory;
+    private final Factory<? extends TestClassProcessor> factory;
     private final long restartEvery;
     private long testCount;
     private TestClassProcessor processor;
     private TestResultProcessor resultProcessor;
 
-    public RestartEveryNTestClassProcessor(TestClassProcessorFactory factory, long restartEvery) {
+    public RestartEveryNTestClassProcessor(Factory<? extends TestClassProcessor> factory, long restartEvery) {
         this.factory = factory;
         this.restartEvery = restartEvery;
     }
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java
index 765b6b3..4d96dc5 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/results/TestLogger.java
@@ -21,15 +21,23 @@ import org.gradle.api.tasks.testing.TestListener;
 import org.gradle.api.tasks.testing.TestResult;
 import org.gradle.logging.ProgressLogger;
 import org.gradle.logging.ProgressLoggerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class TestLogger implements TestListener {
     private final ProgressLoggerFactory factory;
-    private ProgressLogger logger;
+    private final Logger logger;
+    private ProgressLogger progressLogger;
     private long totalTests;
     private long failedTests;
 
     public TestLogger(ProgressLoggerFactory factory) {
+        this(factory, LoggerFactory.getLogger(TestLogger.class));
+    }
+
+    TestLogger(ProgressLoggerFactory factory, Logger logger) {
         this.factory = factory;
+        this.logger = logger;
     }
 
     public void beforeTest(TestDescriptor testDescriptor) {
@@ -38,7 +46,7 @@ public class TestLogger implements TestListener {
     public void afterTest(TestDescriptor testDescriptor, TestResult result) {
         totalTests += result.getTestCount();
         failedTests += result.getFailedTestCount();
-        logger.progress(summary());
+        progressLogger.progress(summary());
     }
 
     private String summary() {
@@ -63,13 +71,16 @@ public class TestLogger implements TestListener {
 
     public void beforeSuite(TestDescriptor suite) {
         if (suite.getParent() == null) {
-            logger = factory.start();
+            progressLogger = factory.start(TestLogger.class.getName());
         }
     }
 
     public void afterSuite(TestDescriptor suite, TestResult result) {
         if (suite.getParent() == null) {
-            logger.completed(failedTests > 0 ? summary() : "");
+            if (failedTests > 0) {
+                logger.error(summary());
+            }
+            progressLogger.completed();
         }
     }
 }
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java
index 14330bf..22b2a04 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessor.java
@@ -19,7 +19,7 @@ package org.gradle.api.internal.tasks.testing.testng;
 import org.gradle.api.GradleException;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
-import org.gradle.api.internal.tasks.testing.junit.CaptureTestOutputTestResultProcessor;
+import org.gradle.api.internal.tasks.testing.processors.CaptureTestOutputTestResultProcessor;
 import org.gradle.api.tasks.testing.testng.TestNGOptions;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.logging.StandardOutputRedirector;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
index 389d2b6..4cd59a5 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
@@ -95,8 +95,19 @@ public class TestNGTestResultProcessorAdapter implements ITestListener, IConfigu
 
     private void onTestFinished(ITestResult iTestResult, TestResult.ResultType resultType) {
         Object testId;
+        TestStartEvent startEvent = null;
         synchronized (lock) {
             testId = tests.remove(iTestResult.getName());
+            if (testId == null) {
+                // This can happen when a method fails which this method depends on 
+                testId = idGenerator.generateId();
+                Object parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
+                startEvent = new TestStartEvent(iTestResult.getStartMillis(), parentId);
+            }
+        }
+        if (startEvent != null) {
+            // Synthesize a start event
+            resultProcessor.started(new DefaultTestMethodDescriptor(testId, iTestResult.getTestClass().getName(), iTestResult.getName()), startEvent);
         }
         if (resultType == TestResult.ResultType.FAILURE) {
             resultProcessor.failure(testId, iTestResult.getThrowable());
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
index 13a52e8..abd382c 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.tasks.testing.worker;
 
 import org.gradle.api.Action;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
@@ -24,12 +25,11 @@ import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
 import org.gradle.process.JavaForkOptions;
 import org.gradle.process.internal.WorkerProcess;
 import org.gradle.process.internal.WorkerProcessBuilder;
-import org.gradle.process.internal.WorkerProcessFactory;
 
 import java.io.File;
 
 public class ForkingTestClassProcessor implements TestClassProcessor {
-    private final WorkerProcessFactory workerFactory;
+    private final Factory<? extends WorkerProcessBuilder> workerFactory;
     private final WorkerTestClassProcessorFactory processorFactory;
     private final JavaForkOptions options;
     private final Iterable<File> classPath;
@@ -38,7 +38,7 @@ public class ForkingTestClassProcessor implements TestClassProcessor {
     private WorkerProcess workerProcess;
     private TestResultProcessor resultProcessor;
 
-    public ForkingTestClassProcessor(WorkerProcessFactory workerFactory, WorkerTestClassProcessorFactory processorFactory, JavaForkOptions options, Iterable<File> classPath, Action<WorkerProcessBuilder> buildConfigAction) {
+    public ForkingTestClassProcessor(Factory<? extends WorkerProcessBuilder> workerFactory, WorkerTestClassProcessorFactory processorFactory, JavaForkOptions options, Iterable<File> classPath, Action<WorkerProcessBuilder> buildConfigAction) {
         this.workerFactory = workerFactory;
         this.processorFactory = processorFactory;
         this.options = options;
@@ -52,7 +52,7 @@ public class ForkingTestClassProcessor implements TestClassProcessor {
 
     public void processTestClass(TestClassRunInfo testClass) {
         if (remoteProcessor == null) {
-            WorkerProcessBuilder builder = workerFactory.newProcess();
+            WorkerProcessBuilder builder = workerFactory.create();
             builder.applicationClasspath(classPath);
             builder.setLoadApplicationInSystemClassLoader(true);
             builder.worker(new TestWorker(processorFactory));
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java
index 8b59148..d2d3e12 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/Manifest.java
@@ -20,6 +20,9 @@ import groovy.lang.Closure;
 import java.io.Writer;
 import java.util.Map;
 
+/**
+ * Represents the manifest file of a JAR file.
+ */
 public interface Manifest {
     /**
      * Returns the main attributes of the manifest.
@@ -46,6 +49,7 @@ public interface Manifest {
     Manifest attributes(Map<String, ?> attributes) throws ManifestException;
 
     /**
+     * Adds content to the given section of the manifest.
      * 
      * @param attributes The values to add to the section. The values can be any object. For evaluating the value objects
      * their {@link Object#toString()} method is used. This is done lazily either before writing or when {@link #getEffectiveManifest()}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeDetails.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeDetails.java
index c994700..4d63e09 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeDetails.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeDetails.java
@@ -15,6 +15,9 @@
  */
 package org.gradle.api.java.archives;
 
+/**
+ * Details of a value being merged from two different manifests.
+ */
 public interface ManifestMergeDetails {
     /**
      * Returns the section this merge details belongs to.
@@ -22,7 +25,7 @@ public interface ManifestMergeDetails {
     String getSection();
 
     /**
-     * Returns the key that is to be merged
+     * Returns the key that is to be merged.
      */
     String getKey();
 
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java
index 8165b07..4251477 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/ManifestMergeSpec.java
@@ -18,6 +18,9 @@ package org.gradle.api.java.archives;
 import groovy.lang.Closure;
 import org.gradle.api.Action;
 
+/**
+ * Specifies how the entries of multiple manifests should be merged together.
+ */
 public interface ManifestMergeSpec {
     /**
      * Adds a merge path to a manifest that should be merged into the base manifest. A merge path can be either another
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java
index e12494c..b583ffc 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/internal/DefaultManifest.java
@@ -163,7 +163,12 @@ public class DefaultManifest implements org.gradle.api.java.archives.Manifest {
             if (file.getParentFile() != null) {
                 file.getParentFile().mkdirs();
             }
-            return writeTo(new FileWriter(file));
+            FileWriter writer = new FileWriter(file);
+            try {
+                return writeTo(writer);
+            } finally {
+                writer.close();
+            }
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
@@ -197,7 +202,13 @@ public class DefaultManifest implements org.gradle.api.java.archives.Manifest {
     private void read(Object manifestPath) {
         File manifestFile = fileResolver.resolve(manifestPath);
         try {
-            Manifest antManifest = new Manifest(new FileReader(manifestFile));
+            FileReader reader = new FileReader(manifestFile);
+            Manifest antManifest;
+            try {
+                antManifest = new Manifest(reader);
+            } finally {
+                reader.close();
+            }
             addAntManifestToAttributes(antManifest);
             addAntManifestToSections(antManifest);
         } catch (ManifestException e) {
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/package-info.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/package-info.java
new file mode 100644
index 0000000..8c1797b
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/java/archives/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for working with JAR manifests.
+ */
+package org.gradle.api.java.archives;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy
index c820d83..854cb85 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/BasePlugin.groovy
@@ -54,7 +54,7 @@ class BasePlugin implements Plugin<Project> {
 
     private Task addAssemble(Project project) {
         Task assembleTask = project.tasks.add(ASSEMBLE_TASK_NAME);
-        assembleTask.description = "Builds all Jar, War, Zip, and Tar archives.";
+        assembleTask.description = "Assembles all Jar, War, Zip, and Tar archives.";
         assembleTask.group = BUILD_GROUP
         assembleTask.dependsOn { project.tasks.withType(AbstractArchiveTask.class).all }
     }
@@ -102,7 +102,7 @@ class BasePlugin implements Plugin<Project> {
 
     private void configureBuildConfigurationRule(Project project) {
         String prefix = "build";
-        String description = "Pattern: ${prefix}<ConfigurationName>: Builds the artifacts belonging to a configuration."
+        String description = "Pattern: ${prefix}<ConfigurationName>: Assembles the artifacts of a configuration."
         Rule rule = [
                 getDescription: {
                     description
@@ -126,7 +126,7 @@ class BasePlugin implements Plugin<Project> {
     }
 
     private void configureUploadRules(final Project project) {
-        String description = "Pattern: upload<ConfigurationName>: Uploads the project artifacts of a configuration to a public Gradle repository."
+        String description = "Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration."
         Rule rule = [
                 getDescription: {
                     description
@@ -166,6 +166,6 @@ class BasePlugin implements Plugin<Project> {
                 setDescription("Configuration for the default artifacts.");
 
         configurations.add(Dependency.DEFAULT_CONFIGURATION).extendsFrom(archivesConfiguration).
-                setDescription("Configuration the default artifacts and its dependencies.");
+                setDescription("Configuration for the default artifacts and their dependencies.");
     }
 }
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
index f680776..95b6f15 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/GroovyPlugin.java
@@ -46,7 +46,7 @@ public class GroovyPlugin implements Plugin<Project> {
 
     private void configureGroovydoc(final Project project) {
         Groovydoc groovyDoc = project.getTasks().add(GROOVYDOC_TASK_NAME, Groovydoc.class);
-        groovyDoc.setDescription("Generates the groovydoc for the source code.");
+        groovyDoc.setDescription("Generates the groovydoc for the main source code.");
         groovyDoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
         groovyDoc.setSource(mainGroovy(project.getConvention()).getGroovy());
     }
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java
index 7ecf983..24377e6 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaBasePlugin.java
@@ -19,6 +19,7 @@ package org.gradle.api.plugins;
 import org.gradle.api.*;
 import org.gradle.api.internal.ConventionMapping;
 import org.gradle.api.internal.IConventionAware;
+import org.gradle.api.internal.plugins.ProcessResources;
 import org.gradle.api.tasks.ConventionValue;
 import org.gradle.api.tasks.Copy;
 import org.gradle.api.tasks.SourceSet;
@@ -208,7 +209,6 @@ public class JavaBasePlugin implements Plugin<Project> {
             }
         });
         project.afterEvaluate(new Action<Project>() {
-            @Override
             public void execute(Project project) {
                 project.getTasks().withType(Test.class).allTasks(new Action<Test>() {
                     public void execute(Test test) {
@@ -224,7 +224,6 @@ public class JavaBasePlugin implements Plugin<Project> {
         String debugProp = getTaskPrefixedProperty(test, "debug");
         if (debugProp != null) {
             test.doFirst(new Action<Task>() {
-                @Override
                 public void execute(Task task) {
                     task.getLogger().info("Running tests for remote debugging.");
                 }
@@ -239,7 +238,6 @@ public class JavaBasePlugin implements Plugin<Project> {
             return;
         }
         test.doFirst(new Action<Task>() {
-            @Override
             public void execute(Task task) {
                 test.getLogger().info("Running single tests with pattern: {}", test.getIncludes());
             }
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
index c701836..81fcda0 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPlugin.java
@@ -98,7 +98,7 @@ public class JavaPlugin implements Plugin<Project> {
 
         SourceSet mainSourceSet = pluginConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
         Javadoc javadoc = project.getTasks().add(JAVADOC_TASK_NAME, Javadoc.class);
-        javadoc.setDescription("Generates the javadoc for the source code.");
+        javadoc.setDescription("Generates the javadoc for the main source code.");
         javadoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
         javadoc.setClasspath(mainSourceSet.getClasses().plus(mainSourceSet.getCompileClasspath()));
         javadoc.setSource(mainSourceSet.getAllJava());
@@ -109,7 +109,7 @@ public class JavaPlugin implements Plugin<Project> {
         project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(TEST_TASK_NAME);
         Jar jar = project.getTasks().add(JAR_TASK_NAME, Jar.class);
         jar.getManifest().from(pluginConvention.getManifest());
-        jar.setDescription("Generates a jar archive with all the compiled classes.");
+        jar.setDescription("Assembles a jar archive containing the main classes.");
         jar.setGroup(BasePlugin.BUILD_GROUP);
         jar.from(pluginConvention.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getClasses());
         jar.getMetaInf().from(new Callable() {
@@ -130,25 +130,29 @@ public class JavaPlugin implements Plugin<Project> {
     }
 
     private void configureTest(final Project project, final JavaPluginConvention pluginConvention) {
+        project.getTasks().withType(Test.class).allTasks(new Action<Test>() {
+            public void execute(Test test) {
+                test.getConventionMapping().map("testClassesDir", new Callable<Object>() {
+                    public Object call() throws Exception {
+                        return pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME).getClassesDir();
+                    }
+                });
+                test.getConventionMapping().map("classpath", new Callable<Object>() {
+                    public Object call() throws Exception {
+                        return pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath();
+                    }
+                });
+                test.getConventionMapping().map("testSrcDirs", new Callable<Object>() {
+                    public Object call() throws Exception {
+                        return new ArrayList<File>(pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME)
+                                .getJava().getSrcDirs());
+                    }
+                });
+            }
+        });
         Test test = project.getTasks().add(TEST_TASK_NAME, Test.class);
         test.setDescription("Runs the unit tests.");
         test.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
-        test.getConventionMapping().map("testClassesDir", new Callable<Object>() {
-            public Object call() throws Exception {
-                return pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME).getClassesDir();
-            }
-        });
-        test.getConventionMapping().map("classpath", new Callable<Object>() {
-            public Object call() throws Exception {
-                return pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME).getRuntimeClasspath();
-            }
-        });
-        test.getConventionMapping().map("testSrcDirs", new Callable<Object>() {
-            public Object call() throws Exception {
-                return new ArrayList<File>(pluginConvention.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME)
-                        .getJava().getSrcDirs());
-            }
-        });
     }
 
     void configureConfigurations(final Project project) {
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy
index 54f5297..ad794fc 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/JavaPluginConvention.groovy
@@ -67,7 +67,7 @@ class JavaPluginConvention {
 
     JavaPluginConvention(Project project) {
         this.project = project
-        def classGenerator = project.serviceRegistryFactory.get(ClassGenerator)
+        def classGenerator = project.services.get(ClassGenerator)
         sourceSets = classGenerator.newInstance(DefaultSourceSetContainer.class, project.fileResolver, project.tasks, classGenerator)
         dependencyCacheDirName = 'dependency-cache'
         docsDirName = 'docs'
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ProcessResources.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ProcessResources.java
deleted file mode 100644
index 0faf8ec..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ProcessResources.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.plugins;
-
-import org.gradle.api.internal.tasks.compile.SimpleStaleClassCleaner;
-import org.gradle.api.internal.tasks.compile.StaleClassCleaner;
-import org.gradle.api.tasks.Copy;
-
-public class ProcessResources extends Copy {
-    @Override
-    protected void copy() {
-        StaleClassCleaner cleaner = new SimpleStaleClassCleaner(getOutputs());
-        cleaner.setDestinationDir(getDestinationDir());
-        cleaner.execute();
-        super.copy();
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java
index 1ea83ba..edb9cd1 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/plugins/ReportingBasePluginConvention.java
@@ -22,7 +22,7 @@ import java.io.File;
 
 /**
  * <p>A {@code BasePluginConvention} defines the convention properties and methods used by the {@link
- * ReportingBasePlugin}</p>
+ * ReportingBasePlugin}.</p>
  */
 public class ReportingBasePluginConvention {
     private String reportsDirName = "reports";
@@ -51,7 +51,7 @@ public class ReportingBasePluginConvention {
     }
 
     /**
-     * Returns the directory containing all reports for this project
+     * Returns the directory containing all reports for this project.
      *
      * @return The reports directory. Never returns null.
      */
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
index b2c7683..b18a2bc 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/SourceSet.java
@@ -134,7 +134,7 @@ public interface SourceSet {
     SourceSet java(Closure configureClosure);
 
     /**
-     * All Java source for this source set. This includes, for example, source which is directly compiled, and source
+     * All Java source files for this source set. This includes, for example, source which is directly compiled, and source
      * which is indirectly compiled through joint compilation.
      *
      * @return the Java source. Never returns null.
@@ -142,7 +142,7 @@ public interface SourceSet {
     FileTree getAllJava();
 
     /**
-     * All source for this source set.
+     * All source files for this source set.
      *
      * @return the source. Never returns null.
      */
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
index 684e3fc..77019f2 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/Jar.groovy
@@ -21,11 +21,13 @@ import org.gradle.api.internal.file.MapFileTree
 import org.gradle.api.java.archives.internal.DefaultManifest
 import org.gradle.util.ConfigureUtil
 import org.gradle.api.internal.file.copy.CopySpecImpl
+import org.gradle.api.file.FileCopyDetails
 
 /**
-* @author Hans Dockter
-*/
-
+ * Assembles a JAR archive.
+ *
+ * @author Hans Dockter
+ */
 public class Jar extends Zip {
     public static final String DEFAULT_EXTENSION = 'jar'
 
@@ -45,6 +47,11 @@ public class Jar extends Zip {
             }
             manifestSource
         }
+        copyAction.mainSpec.eachFile { FileCopyDetails details ->
+            if (details.path.equalsIgnoreCase('META-INF/MANIFEST.MF')) {
+                details.exclude()
+            }
+        }
     }
 
     public DefaultManifest getManifest() {
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/War.groovy b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/War.groovy
index a485acf..ac93a1b 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/War.groovy
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/bundling/War.groovy
@@ -25,6 +25,8 @@ import org.gradle.util.ConfigureUtil
 import org.gradle.api.internal.file.copy.CopySpecImpl
 
 /**
+ * Assembles a WAR archive.
+ * 
  * @author Hans Dockter
  */
 class War extends Jar {
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractCompile.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractCompile.java
index e13a03b..5cc5c5a 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractCompile.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AbstractCompile.java
@@ -20,6 +20,9 @@ import org.gradle.api.tasks.*;
 
 import java.io.File;
 
+/**
+ * The base class for all JVM-based language compilation tasks.
+ */
 public abstract class AbstractCompile extends SourceTask {
     private File destinationDir;
     private String sourceCompatibility;
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AntDepend.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AntDepend.java
deleted file mode 100644
index 9185555..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/AntDepend.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.api.tasks.compile;
-
-import org.apache.tools.ant.taskdefs.optional.depend.Depend;
-import org.apache.tools.ant.types.Path;
-import org.apache.tools.ant.BuildException;
-
-public class AntDepend extends Depend {
-    private Path src;
-
-    public Path createSrc() {
-        if (src == null) {
-            src = new Path(getProject());
-        }
-        return src.createPath();
-    }
-
-    @Override
-    public void execute() throws BuildException {
-        setSrcdir(src);
-        super.execute();
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
index 6e1d7c7..7003dc9 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/Compile.java
@@ -16,7 +16,8 @@
 
 package org.gradle.api.tasks.compile;
 
-import org.gradle.api.internal.project.AntBuilderFactory;
+import org.gradle.api.AntBuilder;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.tasks.compile.AntJavaCompiler;
 import org.gradle.api.internal.tasks.compile.IncrementalJavaCompiler;
 import org.gradle.api.internal.tasks.compile.JavaCompiler;
@@ -28,6 +29,8 @@ import org.gradle.api.tasks.WorkResult;
 import java.io.File;
 
 /**
+ * Compiles Java source files.
+ * 
  * @author Hans Dockter
  */
 public class Compile extends AbstractCompile {
@@ -37,8 +40,8 @@ public class Compile extends AbstractCompile {
     private File dependencyCacheDir;
 
     public Compile() {
-        AntBuilderFactory antBuilderFactory = getServices().get(AntBuilderFactory.class);
-        javaCompiler = new IncrementalJavaCompiler(new AntJavaCompiler(antBuilderFactory), antBuilderFactory, getOutputs());
+        Factory<? extends AntBuilder> antBuilderFactory = getServices().getFactory(AntBuilder.class);
+        javaCompiler = new IncrementalJavaCompiler(new AntJavaCompiler((Factory) antBuilderFactory), antBuilderFactory, getOutputs());
     }
 
     @TaskAction
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java
index 1359674..7244c09 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/compile/GroovyCompile.java
@@ -33,6 +33,8 @@ import java.util.Collection;
 import java.util.List;
 
 /**
+ * Compiles Groovy and Java source files.
+ *
  * @author Hans Dockter
  */
 public class GroovyCompile extends AbstractCompile {
@@ -47,7 +49,6 @@ public class GroovyCompile extends AbstractCompile {
     }
 
     protected void compile() {
-        // todo We need to understand why it is not good enough to put groovy and ant in the task classpath but also Junit. As we don't understand we put the whole testCompile in it right now. It doesn't hurt, but understanding is better :)
         List<File> taskClasspath = new ArrayList<File>(getGroovyClasspath().getFiles());
         throwExceptionIfTaskClasspathIsEmpty(taskClasspath);
         compiler.setSource(getSource());
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
index 661062b..21b3b02 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Groovydoc.java
@@ -27,7 +27,7 @@ import java.io.File;
 import java.util.*;
 
 /**
- * This task generates html api doc for Groovy classes. It uses Groovy's Groovydoc tool for this. Please note that the
+ * Generates HTML API documentation for Groovy and Java classes. It uses Groovy's Groovydoc tool for this. Please note that the
  * Groovydoc tool has some severe limitations at the moment (for example no doc for properties comments). The version of
  * the Groovydoc that is used, is the one from the Groovy defined in the build script. Please note also, that the
  * Groovydoc tool prints to System.out for many of its statements and does circumvents our logging currently.
@@ -228,14 +228,14 @@ public class Groovydoc extends SourceTask {
     }
 
     /**
-     * Returns links to groovydoc/javadoc output at the given URL
+     * Returns links to groovydoc/javadoc output at the given URL.
      */
     public Set<Link> getLinks() {
         return Collections.unmodifiableSet(links);
     }
 
     /**
-     * Sets links to groovydoc/javadoc output at the given URL
+     * Sets links to groovydoc/javadoc output at the given URL.
      *
      * @param links The links to set
      * @see #link(String, String[])
@@ -245,7 +245,7 @@ public class Groovydoc extends SourceTask {
     }
 
     /**
-     * Add links to groovydoc/javadoc output at the given URL
+     * Add links to groovydoc/javadoc output at the given URL.
      *
      * @param url Base URL of external site
      * @param packages list of package prefixes
@@ -262,6 +262,8 @@ public class Groovydoc extends SourceTask {
         private String url;
 
         /**
+         * Constructs a {@code Link}.
+         *
          * @param url Base URL of external site
          * @param packages list of package prefixes
          */
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
index a7188ee..f4f7bf0 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/javadoc/Javadoc.java
@@ -23,7 +23,7 @@ import org.gradle.api.tasks.InputFiles;
 import org.gradle.api.tasks.OutputDirectory;
 import org.gradle.api.tasks.SourceTask;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.external.javadoc.JavadocExecHandleBuilder;
+import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
 import org.gradle.external.javadoc.MinimalJavadocOptions;
 import org.gradle.external.javadoc.StandardJavadocDocletOptions;
 import org.gradle.process.internal.ExecAction;
@@ -36,7 +36,7 @@ import java.util.Iterator;
 import java.util.List;
 
 /**
- * <p>Generates Javadoc from Java source.</p>
+ * <p>Generates HTML API documentation for Java classes.</p>
  *
  * @author Hans Dockter
  */
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
index af433c2..632e777 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/Test.java
@@ -43,7 +43,7 @@ import org.gradle.messaging.actor.ActorFactory;
 import org.gradle.process.JavaForkOptions;
 import org.gradle.process.ProcessForkOptions;
 import org.gradle.process.internal.DefaultJavaForkOptions;
-import org.gradle.process.internal.WorkerProcessFactory;
+import org.gradle.process.internal.WorkerProcessBuilder;
 import org.gradle.util.ConfigureUtil;
 import org.slf4j.LoggerFactory;
 
@@ -54,7 +54,7 @@ import java.util.Map;
 import java.util.Set;
 
 /**
- * A task for executing JUnit (3.8.x or 4.x) or TestNG tests.
+ * Executes tests. Supports JUnit (3.8.x or 4.x) or TestNG tests.
  *
  * @author Hans Dockter
  */
@@ -78,7 +78,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     public Test() {
         testListenerBroadcaster = getServices().get(ListenerManager.class).createAnonymousBroadcaster(
                 TestListener.class);
-        this.testExecuter = new DefaultTestExecuter(getServices().get(WorkerProcessFactory.class), getServices().get(
+        this.testExecuter = new DefaultTestExecuter(getServices().getFactory(WorkerProcessBuilder.class), getServices().get(
                 ActorFactory.class));
         options = new DefaultJavaForkOptions(getServices().get(FileResolver.class));
         options.setEnableAssertions(true);
@@ -333,8 +333,8 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
     }
 
     /**
-     * @return The {@link org.gradle.api.tasks.testing.TestListener} broadcaster.  This broadcaster will send messages
-     *         to all listeners that have been registered with the ListenerManager.
+     * Returns the {@link org.gradle.api.tasks.testing.TestListener} broadcaster.  This broadcaster will send messages
+     * to all listeners that have been registered with the ListenerManager.
      */
     ListenerBroadcast<TestListener> getTestListenerBroadcaster() {
         return testListenerBroadcaster;
@@ -653,6 +653,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
 
     /**
      * Specifies that TestNG should be used to execute the tests.
+     *
      * @param testFrameworkConfigure A closure used to configure the JUint options. This closure is passed an instance
      * of type {@link org.gradle.api.tasks.testing.junit.JUnitOptions}.
      */
@@ -749,6 +750,7 @@ public class Test extends ConventionTask implements JavaForkOptions, PatternFilt
 
     /**
      * Returns the classes files to scan for test classes.
+     *
      * @return The candidate class files.
      */
     @InputFiles
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java
index e523534..005c349 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestDescriptor.java
@@ -22,7 +22,9 @@ package org.gradle.api.tasks.testing;
  */
 public interface TestDescriptor {
     /**
-     * @return The name of the test.  Not guaranteed to be unique.
+     * Returns the name of the test.  Not guaranteed to be unique.
+     *
+     * @return The test name
      */
     String getName();
 
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java
index 740e55d..8d0be7b 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/api/tasks/testing/TestResult.java
@@ -20,17 +20,23 @@ package org.gradle.api.tasks.testing;
  * Describes a test result.
  */
 public interface TestResult {
-    public enum ResultType { SUCCESS, FAILURE, SKIPPED }
+    /**
+     * The final status of a test.
+     */
+    public enum ResultType {SUCCESS, FAILURE, SKIPPED}
 
     /**
-     * @return The type of result.  Generally one wants it to be SUCCESS!
+     * Returns the type of result.  Generally one wants it to be SUCCESS!
+     *
+     * @return The result type.
      */
     ResultType getResultType();
 
     /**
-     * If the test failed with an exception, this will be the exception.  Some
-     * test frameworks do not fail without an exception (JUnit), so in those cases
-     * this method will never return null.  If the resultType is not FAILURE an IllegalStateException is thrown.
+     * If the test failed with an exception, this will be the exception.  Some test frameworks do not fail without an
+     * exception (JUnit), so in those cases this method will never return null.  If the resultType is not FAILURE an
+     * IllegalStateException is thrown.
+     *
      * @return The exception, if any, logged for this test.  If none, a null is returned.
      * @throws IllegalStateException If the result type is anything other than FAILURE.
      */
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/CoreJavadocOptions.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/CoreJavadocOptions.java
index 33bb692..018abf1 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/CoreJavadocOptions.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/CoreJavadocOptions.java
@@ -16,9 +16,7 @@
 
 package org.gradle.external.javadoc;
 
-import org.gradle.external.javadoc.optionfile.JavadocOptionFile;
-import org.gradle.external.javadoc.optionfile.JavadocOptionFileOption;
-import org.gradle.external.javadoc.optionfile.OptionLessJavadocOptionFileOption;
+import org.gradle.external.javadoc.internal.JavadocOptionFile;
 import org.gradle.process.ExecSpec;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.GUtil;
@@ -30,8 +28,7 @@ import java.util.Arrays;
 import java.util.List;
 
 /**
- * 
- *
+ * Provides the core Javadoc Options. That is, provides the options which are not doclet specific.
  *
  * @author Tom Eyckmans
  */
@@ -209,10 +206,10 @@ public abstract class CoreJavadocOptions implements MinimalJavadocOptions
     /**
      * -source release
      * Specifies the version of source code accepted. The following values for release are allowed:
-     * 1.5 	javadoc accepts code containing generics and other language features introduced in JDK 1.5.
+     * 1.5  javadoc accepts code containing generics and other language features introduced in JDK 1.5.
      * The compiler defaults to the 1.5 behavior if the -source flag is not used.
-     * 1.4 	javadoc accepts code containing assertions, which were introduced in JDK 1.4.
-     * 1.3 	javadoc does not support assertions, generics, or other language features introduced after JDK 1.3.
+     * 1.4  javadoc accepts code containing assertions, which were introduced in JDK 1.4.
+     * 1.3  javadoc does not support assertions, generics, or other language features introduced after JDK 1.3.
      *
      * Use the value of release corresponding to that used when compiling the code with javac.
      */
@@ -318,7 +315,7 @@ public abstract class CoreJavadocOptions implements MinimalJavadocOptions
     }
 
     /**
-     * Control the Javadoc output level (-verbose or -quiet
+     * Control the Javadoc output level (-verbose or -quiet).
      */
     private final JavadocOptionFileOption<JavadocOutputLevel> outputLevel;
 
@@ -523,11 +520,11 @@ public abstract class CoreJavadocOptions implements MinimalJavadocOptions
         return optionFile.addStringOption(option, value);
     }
 
-    public <T> JavadocOptionFileOption<T> addEnumOption(String option) {
+    public <T extends Enum<T>> JavadocOptionFileOption<T> addEnumOption(String option) {
         return optionFile.addEnumOption(option);
     }
 
-    public <T> JavadocOptionFileOption<T> addEnumOption(String option, T value) {
+    public <T extends Enum<T>> JavadocOptionFileOption<T> addEnumOption(String option, T value) {
         return optionFile.addEnumOption(option, value);
     }
 
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/JavadocExecHandleBuilder.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/JavadocExecHandleBuilder.java
deleted file mode 100644
index fbbf23b..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/JavadocExecHandleBuilder.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc;
-
-import org.gradle.api.GradleException;
-import org.gradle.process.internal.DefaultExecAction;
-import org.gradle.process.internal.ExecAction;
-import org.gradle.util.GUtil;
-import org.gradle.util.Jvm;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocExecHandleBuilder {
-    private File execDirectory;
-    private MinimalJavadocOptions options;
-    private File optionsFile;
-    private String executable;
-
-    public JavadocExecHandleBuilder execDirectory(File directory) {
-        if (directory == null) {
-            throw new IllegalArgumentException("execDirectory == null!");
-        }
-        if (!directory.exists()) {
-            throw new IllegalArgumentException("execDirectory doesn't exists!");
-        }
-        if (directory.isFile()) {
-            throw new IllegalArgumentException("execDirectory is a file");
-        }
-
-        this.execDirectory = directory;
-        return this;
-    }
-
-    public JavadocExecHandleBuilder options(MinimalJavadocOptions options) {
-        if (options == null) {
-            throw new IllegalArgumentException("options == null!");
-        }
-
-        this.options = options;
-        return this;
-    }
-
-    public JavadocExecHandleBuilder optionsFile(File optionsFile) {
-        this.optionsFile = optionsFile;
-        return this;
-    }
-
-    public ExecAction getExecHandle() {
-        try {
-            options.write(optionsFile);
-        } catch (IOException e) {
-            throw new GradleException("Faild to store javadoc options.", e);
-        }
-
-        ExecAction execAction = new DefaultExecAction();
-        execAction.workingDir(execDirectory);
-        execAction.executable(GUtil.elvis(executable, Jvm.current().getJavadocExecutable()));
-        execAction.args("@" + optionsFile.getAbsolutePath());
-
-        options.contributeCommandLineOptions(execAction);
-
-        return execAction;
-    }
-
-    public String getExecutable() {
-        return executable;
-    }
-
-    public void setExecutable(String executable) {
-        this.executable = executable;
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOptionFileOption.java
new file mode 100644
index 0000000..e824f6b
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/JavadocOptionFileOption.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc;
+
+/**
+ * Represents a Javadoc command-line option.
+ *
+ * @author Tom Eyckmans
+ * @param <T> The type which this option represents.
+ */
+public interface JavadocOptionFileOption<T> extends OptionLessJavadocOptionFileOption<T> {
+    String getOption();
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/MinimalJavadocOptions.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/MinimalJavadocOptions.java
index 18b98fa..62a9df5 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/MinimalJavadocOptions.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/MinimalJavadocOptions.java
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.util.List;
 
 /**
+ * Provides the core Javadoc options.
+ *
  * @author Tom Eyckmans
  */
 public interface MinimalJavadocOptions {
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java
new file mode 100644
index 0000000..aced83b
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/OptionLessJavadocOptionFileOption.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc;
+
+import org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext;
+
+import java.io.IOException;
+
+/**
+ * Represents a Javadoc option.
+ *
+ * @author Tom Eyckmans
+ * @param <T> The type which this option represents.
+ */
+public interface OptionLessJavadocOptionFileOption<T> {
+    T getValue();
+
+    void setValue(T value);
+
+    void write(JavadocOptionFileWriterContext writerContext) throws IOException;
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptions.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptions.java
index 212cbf4..64035a2 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptions.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptions.java
@@ -16,15 +16,16 @@
 
 package org.gradle.external.javadoc;
 
-import org.gradle.external.javadoc.optionfile.JavadocOptionFileOption;
-import org.gradle.external.javadoc.optionfile.GroupsJavadocOptionFileOption;
-import org.gradle.external.javadoc.optionfile.LinksOfflineJavadocOptionFileOption;
-import org.gradle.external.javadoc.optionfile.JavadocOptionFile;
+import org.gradle.external.javadoc.internal.JavadocOptionFile;
+import org.gradle.external.javadoc.internal.GroupsJavadocOptionFileOption;
+import org.gradle.external.javadoc.internal.LinksOfflineJavadocOptionFileOption;
 
 import java.io.File;
 import java.util.*;
 
 /**
+ * Provides the options for the standard Javadoc doclet.
+ * 
  * @author Tom Eyckmans
  */
 public class StandardJavadocDocletOptions extends CoreJavadocOptions implements MinimalJavadocOptions {
@@ -257,7 +258,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
      * Specifies the title to be placed near the top of the overview summary file. The title will be placed as a centered,
      * level-one heading directly beneath the upper navigation bar. The title may contain html tags and white space,
      * though if it does, it must be enclosed in quotes. Any internal quotation marks within title may have to be escaped.
-     * C:> javadoc -doctitle "Java<sup><font size=\"-2\">TM</font></sup>" com.mypackage
+     * C:> javadoc -doctitle "Java<sup><font size=\"-2\">TM</font></sup>" com.mypackage
      */
     private final JavadocOptionFileOption<String> docTitle;
 
@@ -325,7 +326,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
      * you want to link to. Examples are shown below.
      * The package-list file must be found in this directory (otherwise, use -linkoffline).
      * The Javadoc tool reads the package names from the package-list file and then links to those packages at that URL.
-     * When the Javadoc tool is run, the extdocURL value is copied literally into the <A HREF> links that are created.
+     * When the Javadoc tool is run, the extdocURL value is copied literally into the <A HREF> links that are created.
      * Therefore, extdocURL must be the URL to the directory, not to a file.
      * You can use an absolute link for extdocURL to enable your docs to link to a document on any website,
      * or can use a relative link to link only to a relative location. If relative,
@@ -368,7 +369,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
      * then you can run javadoc again on onlya smaller set of changed packages,
      * so that the updated files can be inserted back into the original set. Examples are given below.
      * <p/>
-     * The -linkoffline option takes two arguments -- the first for the string to be embedded in the <a href> links,
+     * The -linkoffline option takes two arguments -- the first for the string to be embedded in the <a href> links,
      * the second telling it where to find package-list:
      * <p/>
      * extdocURL is the absolute or relative URL of the directory containing the external javadoc-generated documentation you want to link to.
@@ -783,7 +784,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
     }
 
     /**
-     * -keywords
+     * -keywords.
      */
     private final JavadocOptionFileOption<Boolean> keyWords;
 
@@ -805,8 +806,8 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
     }
 
     /**
-     * -tag  tagname:Xaoptcmf:"taghead"
-     * -taglet  class
+     * -tag  tagname:Xaoptcmf:"taghead".
+     * -taglet  class.
      */
     private final JavadocOptionFileOption<List<String>> tags;
 
@@ -836,7 +837,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
     }
 
     /**
-     * -tagletpath  tagletpathlist
+     * -tagletpath  tagletpathlist.
      */
     private final JavadocOptionFileOption<List<File>> tagletPath;
 
@@ -858,7 +859,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
     }
 
     /**
-     * -docfilessubdirs
+     * -docfilessubdirs.
      */
     private final JavadocOptionFileOption<Boolean> docFilesSubDirs;
 
@@ -923,9 +924,6 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
         return noQualifier(Arrays.asList(noQualifiers));
     }
 
-    /**
-     * -notimestamp
-     */
     public final JavadocOptionFileOption<Boolean> noTimestamp;
 
     public boolean isNoTimestamp() {
@@ -946,7 +944,7 @@ public class StandardJavadocDocletOptions extends CoreJavadocOptions implements
     }
 
     /**
-     * -nocomment
+     * -nocomment.
      */
     private final JavadocOptionFileOption<Boolean> noComment;
 
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java
new file mode 100644
index 0000000..d269bd9
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractJavadocOptionFileOption.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.external.javadoc.JavadocOptionFileOption;
+
+/**
+ * A base class for {@link org.gradle.external.javadoc.JavadocOptionFileOption} implementations.
+ *
+ * @author Tom Eyckmans
+ * @param <T> The type which this option represents.
+ */
+public abstract class AbstractJavadocOptionFileOption<T> implements JavadocOptionFileOption<T> {
+    protected final String option;
+    protected T value;
+
+    protected AbstractJavadocOptionFileOption(String option) {
+        this(option, null);
+    }
+
+    protected AbstractJavadocOptionFileOption(String option, T value) {
+        if (option == null) {
+            throw new IllegalArgumentException("option == null!");
+        }
+
+        this.option = option;
+        this.value = value;
+    }
+
+    public final String getOption() {
+        return option;
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+    public void setValue(T value) {
+        this.value = value;
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java
new file mode 100644
index 0000000..30e5070
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/AbstractListJavadocOptionFileOption.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.util.List;
+import java.io.IOException;
+
+/**
+ * A base class for {@link org.gradle.external.javadoc.JavadocOptionFileOption} implementations whose value
+ * is a {@code List}.
+ *
+ * @author Tom Eyckmans
+ * @param <T> The type which this option represents.
+ */
+public abstract class AbstractListJavadocOptionFileOption<T extends List> extends AbstractJavadocOptionFileOption<T> {
+    protected String joinBy;
+
+    protected AbstractListJavadocOptionFileOption(String option, String joinBy) {
+        super(option);
+        this.joinBy = joinBy;
+    }
+
+    protected AbstractListJavadocOptionFileOption(String option, T value, String joinBy) {
+        super(option, value);
+        this.joinBy = joinBy;
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+    public void setValue(T value) {
+        if ( value == null ) {
+            this.value.clear();
+        }
+        else {
+            this.value = value;
+        }
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null && !value.isEmpty() ) {
+            writeCollectionValue(writerContext);
+        }
+    }
+
+    protected abstract void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException;
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java
new file mode 100644
index 0000000..7d6ff2c
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOption.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.IOException;
+
+/**
+ * A {@link org.gradle.external.javadoc.JavadocOptionFileOption} whose value is a boolean.
+ * 
+ * @author Tom Eyckmans
+ */
+public class BooleanJavadocOptionFileOption extends AbstractJavadocOptionFileOption<Boolean> {
+    protected BooleanJavadocOptionFileOption(String option) {
+        super(option);
+    }
+
+    protected BooleanJavadocOptionFileOption(String option, Boolean value) {
+        super(option, value);
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null && value) {
+            writerContext.writeOption(option);
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java
new file mode 100644
index 0000000..c4d9625
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOption.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ * @param <T> The type which this option represents.
+ */
+public class EnumJavadocOptionFileOption<T> extends AbstractJavadocOptionFileOption<T> {
+    public EnumJavadocOptionFileOption(String option) {
+        super(option);
+    }
+
+    public EnumJavadocOptionFileOption(String option, T value) {
+        super(option, value);
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null ) {
+            writerContext.writeOption(value.toString().toLowerCase());
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java
new file mode 100644
index 0000000..ce6c7be
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOption.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A {@link org.gradle.external.javadoc.JavadocOptionFileOption} whose value is a file.
+ * 
+ * @author Tom Eyckmans
+ */
+public class FileJavadocOptionFileOption extends AbstractJavadocOptionFileOption<File> {
+    protected FileJavadocOptionFileOption(String option) {
+        super(option);
+    }
+
+    protected FileJavadocOptionFileOption(String option, File value) {
+        super(option, value);
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null ) {
+            writerContext.writeValueOption(option, value.getAbsolutePath());
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java
new file mode 100644
index 0000000..54f1fc5
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOption.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.util.GUtil;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A {@link org.gradle.external.javadoc.JavadocOptionFileOption} which represents the -groups command line
+ * option.
+ *
+ * @author Tom Eyckmans
+ */
+public class GroupsJavadocOptionFileOption extends AbstractJavadocOptionFileOption<Map<String, List<String>>> {
+    public GroupsJavadocOptionFileOption(String option) {
+        super(option, new LinkedHashMap<String, List<String>>());
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null && !value.isEmpty() ) {
+            for ( final String group : value.keySet() ) {
+                final List<String> groupPackages = value.get(group);
+
+                writerContext
+                    .writeOptionHeader(option)
+                    .write(
+                        new StringBuffer()
+                            .append("\"")
+                            .append(group)
+                            .append("\"")
+                            .toString())
+                    .write(" ")
+                    .write(
+                        new StringBuffer()
+                            .append("\"")
+                            .append(GUtil.join(groupPackages, ":"))
+                            .append("\"")
+                            .toString())
+                    .newLine();
+            }
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java
new file mode 100644
index 0000000..2c0a2b9
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilder.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.api.GradleException;
+import org.gradle.external.javadoc.MinimalJavadocOptions;
+import org.gradle.process.internal.DefaultExecAction;
+import org.gradle.process.internal.ExecAction;
+import org.gradle.util.GUtil;
+import org.gradle.util.Jvm;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocExecHandleBuilder {
+    private File execDirectory;
+    private MinimalJavadocOptions options;
+    private File optionsFile;
+    private String executable;
+
+    public JavadocExecHandleBuilder execDirectory(File directory) {
+        if (directory == null) {
+            throw new IllegalArgumentException("execDirectory == null!");
+        }
+        if (!directory.exists()) {
+            throw new IllegalArgumentException("execDirectory doesn't exists!");
+        }
+        if (directory.isFile()) {
+            throw new IllegalArgumentException("execDirectory is a file");
+        }
+
+        this.execDirectory = directory;
+        return this;
+    }
+
+    public JavadocExecHandleBuilder options(MinimalJavadocOptions options) {
+        if (options == null) {
+            throw new IllegalArgumentException("options == null!");
+        }
+
+        this.options = options;
+        return this;
+    }
+
+    public JavadocExecHandleBuilder optionsFile(File optionsFile) {
+        this.optionsFile = optionsFile;
+        return this;
+    }
+
+    public ExecAction getExecHandle() {
+        try {
+            options.write(optionsFile);
+        } catch (IOException e) {
+            throw new GradleException("Faild to store javadoc options.", e);
+        }
+
+        ExecAction execAction = new DefaultExecAction();
+        execAction.workingDir(execDirectory);
+        execAction.executable(GUtil.elvis(executable, Jvm.current().getJavadocExecutable()));
+        execAction.args("@" + optionsFile.getAbsolutePath());
+
+        options.contributeCommandLineOptions(execAction);
+
+        return execAction;
+    }
+
+    public String getExecutable() {
+        return executable;
+    }
+
+    public void setExecutable(String executable) {
+        this.executable = executable;
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFile.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFile.java
new file mode 100644
index 0000000..1af9711
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFile.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.external.javadoc.JavadocOptionFileOption;
+import org.gradle.external.javadoc.OptionLessJavadocOptionFileOption;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocOptionFile {
+    private final Map<String, JavadocOptionFileOption> options;
+
+    private final OptionLessJavadocOptionFileOption<List<String>> sourceNames;
+
+    public JavadocOptionFile() {
+        options = new HashMap<String, JavadocOptionFileOption>();
+        sourceNames = new OptionLessStringsJavadocOptionFileOption();
+    }
+
+    public OptionLessJavadocOptionFileOption<List<String>> getSourceNames() {
+        return sourceNames;
+    }
+
+    Map<String, JavadocOptionFileOption> getOptions() {
+        return Collections.unmodifiableMap(options);
+    }
+
+    public <T> JavadocOptionFileOption<T> addOption(JavadocOptionFileOption<T> option) {
+        if (option == null) {
+            throw new IllegalArgumentException("option == null!");
+        }
+
+        options.put(option.getOption(), option);
+
+        return option;
+    }
+
+    public JavadocOptionFileOption<String> addStringOption(String option) {
+        return addStringOption(option, null);
+    }
+
+    public JavadocOptionFileOption<String> addStringOption(String option, String value) {
+        return addOption(new StringJavadocOptionFileOption(option, value));
+    }
+
+    public <T> JavadocOptionFileOption<T> addEnumOption(String option) {
+        return addEnumOption(option, null);
+    }
+
+    public <T> JavadocOptionFileOption<T> addEnumOption(String option, T value) {
+        return addOption(new EnumJavadocOptionFileOption<T>(option, value));
+    }
+
+    public JavadocOptionFileOption<List<File>> addPathOption(String option) {
+        return addPathOption(option, System.getProperty("path.separator"));
+    }
+
+    public JavadocOptionFileOption<List<File>> addPathOption(String option, String joinBy) {
+        return addOption(new PathJavadocOptionFileOption(option, joinBy));
+    }
+
+    public JavadocOptionFileOption<List<String>> addStringsOption(String option) {
+        return addStringsOption(option, System.getProperty("path.separator"));
+    }
+
+    public JavadocOptionFileOption<List<String>> addStringsOption(String option, String joinBy) {
+        return addOption(new StringsJavadocOptionFileOption(option, new ArrayList<String>(), joinBy));
+    }
+
+    public JavadocOptionFileOption<List<String>> addMultilineStringsOption(String option) {
+        return addOption(new MultilineStringsJavadocOptionFileOption(option, new ArrayList<String>()));
+    }
+
+    public JavadocOptionFileOption<Boolean> addBooleanOption(String option) {
+        return addBooleanOption(option, false);
+    }
+
+    public JavadocOptionFileOption<Boolean> addBooleanOption(String option, boolean value) {
+        return addOption(new BooleanJavadocOptionFileOption(option, value));
+    }
+
+    public JavadocOptionFileOption<File> addFileOption(String option) {
+        return addFileOption(option, null);
+    }
+
+    public JavadocOptionFileOption<File> addFileOption(String option, File value) {
+        return addOption(new FileJavadocOptionFileOption(option, value));
+    }
+
+    public void write(File optionFile) throws IOException {
+        if (optionFile == null) {
+            throw new IllegalArgumentException("optionFile == null!");
+        }
+
+        final JavadocOptionFileWriter optionFileWriter = new JavadocOptionFileWriter(this);
+
+        optionFileWriter.write(optionFile);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java
new file mode 100644
index 0000000..7570dbd
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.apache.commons.io.IOUtils;
+import org.gradle.external.javadoc.JavadocOptionFileOption;
+
+import java.io.IOException;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.File;
+import java.util.Map;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocOptionFileWriter {
+    private final JavadocOptionFile optionFile;
+
+    public JavadocOptionFileWriter(JavadocOptionFile optionFile) {
+        if (optionFile == null) {
+            throw new IllegalArgumentException("optionFile == null!");
+        }
+        this.optionFile = optionFile;
+    }
+
+    void write(File outputFile) throws IOException {
+        BufferedWriter writer = null;
+        try {
+            final Map<String, JavadocOptionFileOption> options = optionFile.getOptions();
+            writer = new BufferedWriter(new FileWriter(outputFile));
+            JavadocOptionFileWriterContext writerContext = new JavadocOptionFileWriterContext(writer);
+
+            for (final String option : options.keySet()) {
+                options.get(option).write(writerContext);
+            }
+
+            optionFile.getSourceNames().write(writerContext);
+        } finally {
+            IOUtils.closeQuietly(writer);
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java
new file mode 100644
index 0000000..679c9d1
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContext.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocOptionFileWriterContext {
+    private final BufferedWriter writer;
+
+    public JavadocOptionFileWriterContext(BufferedWriter writer) {
+        this.writer = writer;
+    }
+
+    public JavadocOptionFileWriterContext write(String string) throws IOException {
+        writer.write(string);
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext newLine() throws IOException {
+        writer.newLine();
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeOptionHeader(String option) throws IOException {
+        write("-");
+        write(option);
+        write(" ");
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeOption(String option) throws IOException {
+        writeOptionHeader(option);
+        newLine();
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValueOption(String option, String value) throws IOException {
+        writeOptionHeader(option);
+        writeValue(value);
+        newLine();
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValue(String value) throws IOException {
+        write("\'");
+        write(value.replaceAll("\\\\", "\\\\\\\\"));
+        write("\'");
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValueOption(String option, Collection<String> values) throws IOException {
+        for ( final String value : values ) {
+            writeValueOption(option, value);
+        }
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeValuesOption(String option, Collection<String> values, String joinValuesBy) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        Iterator<String> valuesIt = values.iterator();
+        while (valuesIt.hasNext()) {
+            builder.append(valuesIt.next());
+            if (valuesIt.hasNext()) {
+                builder.append(joinValuesBy);
+            }
+        }
+        writeValueOption(option, builder.toString());
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writeMultilineValuesOption(String option, Collection<String> values) throws IOException {
+        for(String value : values) {
+            writeValueOption(option, value);
+        }
+        return this;
+    }
+
+    public JavadocOptionFileWriterContext writePathOption(String option, Collection<File> files, String joinValuesBy) throws IOException {
+        StringBuilder builder = new StringBuilder();
+        Iterator<File> filesIt = files.iterator();
+        while ( filesIt.hasNext() ) {
+            builder.append(filesIt.next().getAbsolutePath());
+            if (filesIt.hasNext()) {
+                builder.append(joinValuesBy);
+            }
+        }
+        writeValueOption(option, builder.toString());
+        return this;
+    }
+
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java
new file mode 100644
index 0000000..9183c1e
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOption.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.external.javadoc.JavadocOfflineLink;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class LinksOfflineJavadocOptionFileOption extends AbstractJavadocOptionFileOption<List<JavadocOfflineLink>> {
+    public LinksOfflineJavadocOptionFileOption(String option) {
+        super(option, new ArrayList<JavadocOfflineLink>());
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null && !value.isEmpty() ) {
+            for ( final JavadocOfflineLink offlineLink : value ) {
+                writerContext.writeValueOption(option, offlineLink.toString());
+            }
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java
new file mode 100644
index 0000000..3bacad9
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOption.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+
+/**
+ * @author Melanie Pfautz
+ */
+public class MultilineStringsJavadocOptionFileOption extends AbstractListJavadocOptionFileOption<List<String>> {
+
+   // We should never attempt to join strings so if you see this, there's a problem
+    private static final String JOIN_BY = "Not Used!";
+
+    protected MultilineStringsJavadocOptionFileOption(String option) {
+        super(option, new ArrayList<String>(), JOIN_BY);
+    }
+
+    protected MultilineStringsJavadocOptionFileOption(String option, List<String> value) {
+        super(option, value, JOIN_BY);
+    }
+
+    public void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null && !value.isEmpty() ) {
+            writerContext.writeMultilineValuesOption(option, value);
+       }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java
new file mode 100644
index 0000000..83899d7
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOption.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.external.javadoc.OptionLessJavadocOptionFileOption;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class OptionLessStringsJavadocOptionFileOption implements OptionLessJavadocOptionFileOption<List<String>> {
+    private List<String> value;
+
+    public OptionLessStringsJavadocOptionFileOption() {
+        value = new ArrayList<String>();
+    }
+
+    public OptionLessStringsJavadocOptionFileOption(List<String> value) {
+        this.value = value;
+    }
+
+    public List<String> getValue() {
+        return value;
+    }
+
+    public void setValue(List<String> value) {
+        if ( value == null ) {
+            this.value.clear();
+        }
+        else {
+            this.value = value;
+        }
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if ( value != null && !value.isEmpty() ) {
+            for ( String singleValue : value ) {
+                writerContext.writeValue(singleValue);
+                writerContext.newLine();
+            }
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java
new file mode 100644
index 0000000..599248c
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOption.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class PathJavadocOptionFileOption extends AbstractListJavadocOptionFileOption<List<File>> {
+
+    public PathJavadocOptionFileOption(String option, String joinBy) {
+        super(option, new ArrayList<File>(), joinBy);
+    }
+
+    public PathJavadocOptionFileOption(String option, List<File> value, String joinBy) {
+        super(option, value, joinBy);
+    }
+
+    public void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException {
+        writerContext.writePathOption(option, value, joinBy);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java
new file mode 100644
index 0000000..4b4cecc
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOption.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class StringJavadocOptionFileOption extends AbstractJavadocOptionFileOption<String> {
+    public StringJavadocOptionFileOption(String option) {
+        super(option);
+    }
+
+    public StringJavadocOptionFileOption(String option, String value) {
+        super(option, value);
+    }
+
+    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
+        if (value != null) {
+            writerContext.writeValueOption(option, value);
+        }
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java
new file mode 100644
index 0000000..bb60153
--- /dev/null
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOption.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class StringsJavadocOptionFileOption extends AbstractListJavadocOptionFileOption<List<String>> {
+    protected StringsJavadocOptionFileOption(String option, String joinBy) {
+        super(option, new ArrayList<String>(), joinBy);
+    }
+
+    protected StringsJavadocOptionFileOption(String option, List<String> value, String joinBy) {
+        super(option, value, joinBy);
+    }
+
+    public void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException {
+        writerContext.writeValuesOption(option, value, joinBy);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/AbstractJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/AbstractJavadocOptionFileOption.java
deleted file mode 100644
index cae36c3..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/AbstractJavadocOptionFileOption.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-/**
- * @author Tom Eyckmans
- */
-public abstract class AbstractJavadocOptionFileOption<T> implements JavadocOptionFileOption<T> {
-    protected final String option;
-    protected T value;
-
-    protected AbstractJavadocOptionFileOption(String option) {
-        this(option, null);
-    }
-
-    protected AbstractJavadocOptionFileOption(String option, T value) {
-        if (option == null) {
-            throw new IllegalArgumentException("option == null!");
-        }
-
-        this.option = option;
-        this.value = value;
-    }
-
-    public final String getOption() {
-        return option;
-    }
-
-    public T getValue() {
-        return value;
-    }
-
-    public void setValue(T value) {
-        this.value = value;
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/AbstractListJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/AbstractListJavadocOptionFileOption.java
deleted file mode 100644
index f761bdd..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/AbstractListJavadocOptionFileOption.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.util.List;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public abstract class AbstractListJavadocOptionFileOption<T extends List> extends AbstractJavadocOptionFileOption<T> {
-    protected String joinBy;
-
-    protected AbstractListJavadocOptionFileOption(String option, String joinBy) {
-        super(option);
-        this.joinBy = joinBy;
-    }
-
-    protected AbstractListJavadocOptionFileOption(String option, T value, String joinBy) {
-        super(option, value);
-        this.joinBy = joinBy;
-    }
-
-    public T getValue() {
-        return value;
-    }
-
-    public void setValue(T value) {
-        if ( value == null ) {
-            this.value.clear();
-        }
-        else {
-            this.value = value;
-        }
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && !value.isEmpty() ) {
-            writeCollectionValue(writerContext);
-        }
-    }
-
-    protected abstract void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException;
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/BooleanJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/BooleanJavadocOptionFileOption.java
deleted file mode 100644
index e7dc670..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/BooleanJavadocOptionFileOption.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class BooleanJavadocOptionFileOption extends AbstractJavadocOptionFileOption<Boolean> {
-    protected BooleanJavadocOptionFileOption(String option) {
-        super(option);
-    }
-
-    protected BooleanJavadocOptionFileOption(String option, Boolean value) {
-        super(option, value);
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && value) {
-            writerContext.writeOption(option);
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/EnumJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/EnumJavadocOptionFileOption.java
deleted file mode 100644
index 6c96146..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/EnumJavadocOptionFileOption.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class EnumJavadocOptionFileOption<T> extends AbstractJavadocOptionFileOption<T> {
-    public EnumJavadocOptionFileOption(String option) {
-        super(option);
-    }
-
-    public EnumJavadocOptionFileOption(String option, T value) {
-        super(option, value);
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null ) {
-            writerContext.writeOption(value.toString().toLowerCase());
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/FileJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/FileJavadocOptionFileOption.java
deleted file mode 100644
index a0a6c51..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/FileJavadocOptionFileOption.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class FileJavadocOptionFileOption extends AbstractJavadocOptionFileOption<File> {
-    protected FileJavadocOptionFileOption(String option) {
-        super(option);
-    }
-
-    protected FileJavadocOptionFileOption(String option, File value) {
-        super(option, value);
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null ) {
-            writerContext.writeValueOption(option, value.getAbsolutePath());
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/GroupsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/GroupsJavadocOptionFileOption.java
deleted file mode 100644
index 9d0acea..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/GroupsJavadocOptionFileOption.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.gradle.util.GUtil;
-
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author Tom Eyckmans
- */
-public class GroupsJavadocOptionFileOption extends AbstractJavadocOptionFileOption<Map<String, List<String>>> {
-    public GroupsJavadocOptionFileOption(String option) {
-        super(option, new LinkedHashMap<String, List<String>>());
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && !value.isEmpty() ) {
-            for ( final String group : value.keySet() ) {
-                final List<String> groupPackages = value.get(group);
-
-                writerContext
-                    .writeOptionHeader(option)
-                    .write(
-                        new StringBuffer()
-                            .append("\"")
-                            .append(group)
-                            .append("\"")
-                            .toString())
-                    .write(" ")
-                    .write(
-                        new StringBuffer()
-                            .append("\"")
-                            .append(GUtil.join(groupPackages, ":"))
-                            .append("\"")
-                            .toString())
-                    .newLine();
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFile.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFile.java
deleted file mode 100644
index 4fa20fb..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFile.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocOptionFile {
-    private final Map<String, JavadocOptionFileOption> options;
-
-    private final OptionLessJavadocOptionFileOption<List<String>> sourceNames;
-
-    public JavadocOptionFile() {
-        options = new HashMap<String, JavadocOptionFileOption>();
-        sourceNames = new OptionLessStringsJavadocOptionFileOption();
-    }
-
-    public OptionLessJavadocOptionFileOption<List<String>> getSourceNames() {
-        return sourceNames;
-    }
-
-    Map<String, JavadocOptionFileOption> getOptions() {
-        return Collections.unmodifiableMap(options);
-    }
-
-    public <T> JavadocOptionFileOption<T> addOption(JavadocOptionFileOption<T> option) {
-        if (option == null) {
-            throw new IllegalArgumentException("option == null!");
-        }
-
-        options.put(option.getOption(), option);
-
-        return option;
-    }
-
-    public JavadocOptionFileOption<String> addStringOption(String option) {
-        return addStringOption(option, null);
-    }
-
-    public JavadocOptionFileOption<String> addStringOption(String option, String value) {
-        return addOption(new StringJavadocOptionFileOption(option, value));
-    }
-
-    public <T> JavadocOptionFileOption<T> addEnumOption(String option) {
-        return addEnumOption(option, null);
-    }
-
-    public <T> JavadocOptionFileOption<T> addEnumOption(String option, T value) {
-        return addOption(new EnumJavadocOptionFileOption<T>(option, value));
-    }
-
-    public JavadocOptionFileOption<List<File>> addPathOption(String option) {
-        return addPathOption(option, System.getProperty("path.separator"));
-    }
-
-    public JavadocOptionFileOption<List<File>> addPathOption(String option, String joinBy) {
-        return addOption(new PathJavadocOptionFileOption(option, joinBy));
-    }
-
-    public JavadocOptionFileOption<List<String>> addStringsOption(String option) {
-        return addStringsOption(option, System.getProperty("path.separator"));
-    }
-
-    public JavadocOptionFileOption<List<String>> addStringsOption(String option, String joinBy) {
-        return addOption(new StringsJavadocOptionFileOption(option, new ArrayList<String>(), joinBy));
-    }
-
-    public JavadocOptionFileOption<List<String>> addMultilineStringsOption(String option) {
-        return addOption(new MultilineStringsJavadocOptionFileOption(option, new ArrayList<String>()));
-    }
-
-    public JavadocOptionFileOption<Boolean> addBooleanOption(String option) {
-        return addBooleanOption(option, false);
-    }
-
-    public JavadocOptionFileOption<Boolean> addBooleanOption(String option, boolean value) {
-        return addOption(new BooleanJavadocOptionFileOption(option, value));
-    }
-
-    public JavadocOptionFileOption<File> addFileOption(String option) {
-        return addFileOption(option, null);
-    }
-
-    public JavadocOptionFileOption<File> addFileOption(String option, File value) {
-        return addOption(new FileJavadocOptionFileOption(option, value));
-    }
-
-    public void write(File optionFile) throws IOException {
-        if (optionFile == null) {
-            throw new IllegalArgumentException("optionFile == null!");
-        }
-
-        final JavadocOptionFileWriter optionFileWriter = new JavadocOptionFileWriter(this);
-
-        optionFileWriter.write(optionFile);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileOption.java
deleted file mode 100644
index c1697f3..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileOption.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-/**
- * @author Tom Eyckmans
- */
-public interface JavadocOptionFileOption<T> extends OptionLessJavadocOptionFileOption<T> {
-
-    String getOption();
-
-
-
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriter.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriter.java
deleted file mode 100644
index 0cb82fb..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-import java.io.File;
-import java.util.Map;
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocOptionFileWriter {
-    private final JavadocOptionFile optionFile;
-
-    public JavadocOptionFileWriter(JavadocOptionFile optionFile) {
-        if (optionFile == null) {
-            throw new IllegalArgumentException("optionFile == null!");
-        }
-        this.optionFile = optionFile;
-    }
-
-    void write(File outputFile) throws IOException {
-        BufferedWriter writer = null;
-        try {
-            final Map<String, JavadocOptionFileOption> options = optionFile.getOptions();
-            writer = new BufferedWriter(new FileWriter(outputFile));
-            JavadocOptionFileWriterContext writerContext = new JavadocOptionFileWriterContext(writer);
-
-            for (final String option : options.keySet()) {
-                options.get(option).write(writerContext);
-            }
-
-            optionFile.getSourceNames().write(writerContext);
-        } finally {
-            IOUtils.closeQuietly(writer);
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriterContext.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriterContext.java
deleted file mode 100644
index 0e2d5d1..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriterContext.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.File;
-import java.util.Collection;
-import java.util.Iterator;
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocOptionFileWriterContext {
-    private final BufferedWriter writer;
-
-    public JavadocOptionFileWriterContext(BufferedWriter writer) {
-        this.writer = writer;
-    }
-
-    public JavadocOptionFileWriterContext write(String string) throws IOException {
-        writer.write(string);
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext newLine() throws IOException {
-        writer.newLine();
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeOptionHeader(String option) throws IOException {
-        write("-");
-        write(option);
-        write(" ");
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeOption(String option) throws IOException {
-        writeOptionHeader(option);
-        newLine();
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValueOption(String option, String value) throws IOException {
-        writeOptionHeader(option);
-        writeValue(value);
-        newLine();
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValue(String value) throws IOException {
-        write("\'");
-        write(value.replaceAll("\\\\", "\\\\\\\\"));
-        write("\'");
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValueOption(String option, Collection<String> values) throws IOException {
-        for ( final String value : values ) {
-            writeValueOption(option, value);
-        }
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeValuesOption(String option, Collection<String> values, String joinValuesBy) throws IOException {
-        StringBuilder builder = new StringBuilder();
-        Iterator<String> valuesIt = values.iterator();
-        while (valuesIt.hasNext()) {
-            builder.append(valuesIt.next());
-            if (valuesIt.hasNext()) {
-                builder.append(joinValuesBy);
-            }
-        }
-        writeValueOption(option, builder.toString());
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writeMultilineValuesOption(String option, Collection<String> values) throws IOException {
-        for(String value : values) {
-            writeValueOption(option, value);
-        }
-        return this;
-    }
-
-    public JavadocOptionFileWriterContext writePathOption(String option, Collection<File> files, String joinValuesBy) throws IOException {
-        StringBuilder builder = new StringBuilder();
-        Iterator<File> filesIt = files.iterator();
-        while ( filesIt.hasNext() ) {
-            builder.append(filesIt.next().getAbsolutePath());
-            if (filesIt.hasNext()) {
-                builder.append(joinValuesBy);
-            }
-        }
-        writeValueOption(option, builder.toString());
-        return this;
-    }
-
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/LinksOfflineJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/LinksOfflineJavadocOptionFileOption.java
deleted file mode 100644
index 2f27f44..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/LinksOfflineJavadocOptionFileOption.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.gradle.external.javadoc.JavadocOfflineLink;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class LinksOfflineJavadocOptionFileOption extends AbstractJavadocOptionFileOption<List<JavadocOfflineLink>> {
-    public LinksOfflineJavadocOptionFileOption(String option) {
-        super(option, new ArrayList<JavadocOfflineLink>());
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && !value.isEmpty() ) {
-            for ( final JavadocOfflineLink offlineLink : value ) {
-                writerContext.writeValueOption(option, offlineLink.toString());
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/MultilineStringsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/MultilineStringsJavadocOptionFileOption.java
deleted file mode 100644
index e732844..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/MultilineStringsJavadocOptionFileOption.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.IOException;
-
-/**
- * @author Melanie Pfautz
- */
-public class MultilineStringsJavadocOptionFileOption extends AbstractListJavadocOptionFileOption<List<String>> {
-
-   // We should never attempt to join strings so if you see this, there's a problem
-    private static final String JOIN_BY = "Not Used!";
-
-    protected MultilineStringsJavadocOptionFileOption(String option) {
-        super(option, new ArrayList<String>(), JOIN_BY);
-    }
-
-    protected MultilineStringsJavadocOptionFileOption(String option, List<String> value) {
-        super(option, value, JOIN_BY);
-    }
-
-    public void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && !value.isEmpty() ) {
-            writerContext.writeMultilineValuesOption(option, value);
-       }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/OptionLessJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/OptionLessJavadocOptionFileOption.java
deleted file mode 100644
index fe4007e..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/OptionLessJavadocOptionFileOption.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public interface OptionLessJavadocOptionFileOption<T> {
-    T getValue();
-
-    void setValue(T value);
-
-    void write(JavadocOptionFileWriterContext writerContext) throws IOException;
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/OptionLessStringsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/OptionLessStringsJavadocOptionFileOption.java
deleted file mode 100644
index 27c372d..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/OptionLessStringsJavadocOptionFileOption.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class OptionLessStringsJavadocOptionFileOption implements OptionLessJavadocOptionFileOption<List<String>> {
-    private List<String> value;
-
-    public OptionLessStringsJavadocOptionFileOption() {
-        value = new ArrayList<String>();
-    }
-
-    public OptionLessStringsJavadocOptionFileOption(List<String> value) {
-        this.value = value;
-    }
-
-    public List<String> getValue() {
-        return value;
-    }
-
-    public void setValue(List<String> value) {
-        if ( value == null ) {
-            this.value.clear();
-        }
-        else {
-            this.value = value;
-        }
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && !value.isEmpty() ) {
-            for ( String singleValue : value ) {
-                writerContext.writeValue(singleValue);
-                writerContext.newLine();
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/PathJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/PathJavadocOptionFileOption.java
deleted file mode 100644
index 0df2a0f..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/PathJavadocOptionFileOption.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.ArrayList;
-
-/**
- * @author Tom Eyckmans
- */
-public class PathJavadocOptionFileOption extends AbstractListJavadocOptionFileOption<List<File>> {
-
-    public PathJavadocOptionFileOption(String option, String joinBy) {
-        super(option, new ArrayList<File>(), joinBy);
-    }
-
-    public PathJavadocOptionFileOption(String option, List<File> value, String joinBy) {
-        super(option, value, joinBy);
-    }
-
-    public void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException {
-        writerContext.writePathOption(option, value, joinBy);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/StringJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/StringJavadocOptionFileOption.java
deleted file mode 100644
index 1393df4..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/StringJavadocOptionFileOption.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class StringJavadocOptionFileOption extends AbstractJavadocOptionFileOption<String> {
-    public StringJavadocOptionFileOption(String option) {
-        super(option);
-    }
-
-    public StringJavadocOptionFileOption(String option, String value) {
-        super(option, value);
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if (value != null) {
-            writerContext.writeValueOption(option, value);
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/StringsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/StringsJavadocOptionFileOption.java
deleted file mode 100644
index 63d25a2..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/StringsJavadocOptionFileOption.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class StringsJavadocOptionFileOption extends AbstractListJavadocOptionFileOption<List<String>> {
-    protected StringsJavadocOptionFileOption(String option, String joinBy) {
-        super(option, new ArrayList<String>(), joinBy);
-    }
-
-    protected StringsJavadocOptionFileOption(String option, List<String> value, String joinBy) {
-        super(option, value, joinBy);
-    }
-
-    public void writeCollectionValue(JavadocOptionFileWriterContext writerContext) throws IOException {
-        writerContext.writeValuesOption(option, value, joinBy);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/TagsJavadocOptionFileOption.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/TagsJavadocOptionFileOption.java
deleted file mode 100644
index 6a1e708..0000000
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/optionfile/TagsJavadocOptionFileOption.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class TagsJavadocOptionFileOption extends AbstractJavadocOptionFileOption<List<String>> {
-    protected TagsJavadocOptionFileOption(String option) {
-        super(option, new ArrayList<String>());
-    }
-
-    public List<String> getValue() {
-        return value;
-    }
-
-    public void setValue(List<String> value) {
-        if ( value == null ) {
-            this.value.clear();
-        }
-        else {
-            this.value = value;
-        }
-    }
-
-    public void write(JavadocOptionFileWriterContext writerContext) throws IOException {
-        if ( value != null && !value.isEmpty() ) {
-            for ( final String tag : value ) {
-                if ( tag.contains(":") || tag.contains("\"") ) {
-                    writerContext.writeValueOption("tag", tag);
-                }
-                else {
-                    writerContext.writeValueOption("taglet", tag);
-                }
-            }
-        }
-    }
-}
diff --git a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/package-info.java b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/package-info.java
index 34ba6c2..90dd824 100644
--- a/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/package-info.java
+++ b/subprojects/gradle-plugins/src/main/groovy/org/gradle/external/javadoc/package-info.java
@@ -16,7 +16,5 @@
 
 /**
  * Classes to run Javadoc.
- *
- * 
  */
 package org.gradle.external.javadoc;
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/CaptureTestOutputTestResultProcessorTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/CaptureTestOutputTestResultProcessorTest.groovy
deleted file mode 100644
index 00b8a86..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/CaptureTestOutputTestResultProcessorTest.groovy
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.tasks.testing.junit
-
-import org.gradle.logging.StandardOutputRedirector
-import spock.lang.Specification
-import org.gradle.api.internal.tasks.testing.*
-
-class CaptureTestOutputTestResultProcessorTest extends Specification {
-    private final TestResultProcessor target = Mock()
-    private final StandardOutputRedirector redirector = Mock()
-    private final CaptureTestOutputTestResultProcessor processor = new CaptureTestOutputTestResultProcessor(target, redirector)
-
-    def capturesStdOutputAndStdErrorWhileTestIsExecuting() {
-        TestDescriptorInternal test = Mock()
-        TestStartEvent startEvent = Mock()
-        TestCompleteEvent completeEvent = Mock()
-        String testId = 'id'
-        _ * test.getId() >> testId
-        def stdoutListener
-        def stderrListener
-
-        when:
-        processor.started(test, startEvent)
-
-        then:
-        1 * target.started(test, startEvent)
-        1 * redirector.redirectStandardOutputTo(!null) >> { args -> stdoutListener = args[0] }
-        1 * redirector.redirectStandardErrorTo(!null) >> { args -> stderrListener = args[0] }
-        1 * redirector.start()
-
-        when:
-        stdoutListener.onOutput('this is stdout')
-        stderrListener.onOutput('this is stderr')
-
-        then:
-        1 * target.output(testId, { TestOutputEvent event ->
-            event.destination == TestOutputEvent.Destination.StdOut && event.message == 'this is stdout'
-        })
-        1 * target.output(testId, { TestOutputEvent event ->
-            event.destination == TestOutputEvent.Destination.StdErr && event.message == 'this is stderr' })
-
-        when:
-        processor.completed(testId, completeEvent)
-
-        then:
-        1 * redirector.stop()
-        1 * target.completed(testId, completeEvent)
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
index eb0bdca..2b7accb 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/junit/JUnitTestClassProcessorTest.groovy
@@ -466,7 +466,7 @@ class JUnitTestClassProcessorTest {
     }
 }
 
-public static class ATestClass {
+public class ATestClass {
     @Test
     public void ok() {
     }
@@ -476,7 +476,7 @@ public static class ATestClass {
     }
 }
 
-public static class ATestClassWithBrokenConstructor {
+public class ATestClassWithBrokenConstructor {
     static RuntimeException failure = new RuntimeException()
 
     def ATestClassWithBrokenConstructor() {
@@ -488,7 +488,7 @@ public static class ATestClassWithBrokenConstructor {
     }
 }
 
-public static class ATestClassWithBrokenBeforeMethod {
+public class ATestClassWithBrokenBeforeMethod {
     static RuntimeException failure = new RuntimeException()
 
     @Before
@@ -501,7 +501,7 @@ public static class ATestClassWithBrokenBeforeMethod {
     }
 }
 
-public static class ATestClassWithBrokenBeforeClassMethod {
+public class ATestClassWithBrokenBeforeClassMethod {
     static RuntimeException failure = new RuntimeException()
 
     @BeforeClass
@@ -514,18 +514,18 @@ public static class ATestClassWithBrokenBeforeClassMethod {
     }
 }
 
-public static class AJunit3TestClass extends TestCase {
+public class AJunit3TestClass extends TestCase {
     public void testOk() {
     }
 }
 
-public static class ATestClassWithSuiteMethod {
+public class ATestClassWithSuiteMethod {
     public static junit.framework.Test suite() {
         return new junit.framework.TestSuite(AJunit3TestClass.class, AJunit3TestClass.class);
     }
 }
 
-public static class ATestClassWithBrokenSuiteMethod {
+public class ATestClassWithBrokenSuiteMethod {
     static RuntimeException failure = new RuntimeException('broken')
 
     public static junit.framework.Test suite() {
@@ -533,7 +533,7 @@ public static class ATestClassWithBrokenSuiteMethod {
     }
 }
 
-public static class ATestSetUpWithBrokenSetUp extends TestSetup {
+public class ATestSetUpWithBrokenSetUp extends TestSetup {
     static RuntimeException failure = new RuntimeException('broken')
 
 
@@ -551,9 +551,9 @@ public static class ATestSetUpWithBrokenSetUp extends TestSetup {
 }
 
 @RunWith(CustomRunner.class)
-public static class ATestClassWithRunner {}
+public class ATestClassWithRunner {}
 
-public static class CustomRunner extends Runner {
+public class CustomRunner extends Runner {
     static RuntimeException failure = new RuntimeException('broken')
     Class<?> type
 
@@ -583,9 +583,9 @@ public static class CustomRunner extends Runner {
 }
 
 @RunWith(CustomRunnerWithBrokenConstructor.class)
-public static class ATestClassWithUnconstructableRunner {}
+public class ATestClassWithUnconstructableRunner {}
 
-public static class CustomRunnerWithBrokenConstructor extends Runner {
+public class CustomRunnerWithBrokenConstructor extends Runner {
     static RuntimeException failure = new RuntimeException()
 
     def CustomRunnerWithBrokenConstructor(Class<?> type) {
@@ -601,7 +601,7 @@ public static class CustomRunnerWithBrokenConstructor extends Runner {
     }
 }
 
-public static class ATestClassWhichCannotBeLoaded {
+public class ATestClassWhichCannotBeLoaded {
     static {
         throw new NoClassDefFoundError()
     }
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy
new file mode 100644
index 0000000..80525b1
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/CaptureTestOutputTestResultProcessorTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.processors
+
+import org.gradle.logging.StandardOutputRedirector
+import spock.lang.Specification
+import org.gradle.api.internal.tasks.testing.*
+
+class CaptureTestOutputTestResultProcessorTest extends Specification {
+    private final TestResultProcessor target = Mock()
+    private final StandardOutputRedirector redirector = Mock()
+    private final CaptureTestOutputTestResultProcessor processor = new CaptureTestOutputTestResultProcessor(target, redirector)
+
+    def capturesStdOutputAndStdErrorWhileTestIsExecuting() {
+        TestDescriptorInternal test = Mock()
+        TestStartEvent startEvent = Mock()
+        TestCompleteEvent completeEvent = Mock()
+        String testId = 'id'
+        _ * test.getId() >> testId
+        def stdoutListener
+        def stderrListener
+
+        when:
+        processor.started(test, startEvent)
+
+        then:
+        1 * target.started(test, startEvent)
+        1 * redirector.redirectStandardOutputTo(!null) >> { args -> stdoutListener = args[0] }
+        1 * redirector.redirectStandardErrorTo(!null) >> { args -> stderrListener = args[0] }
+        1 * redirector.start()
+
+        when:
+        stdoutListener.onOutput('this is stdout')
+        stderrListener.onOutput('this is stderr')
+
+        then:
+        1 * target.output(testId, { TestOutputEvent event ->
+            event.destination == TestOutputEvent.Destination.StdOut && event.message == 'this is stdout'
+        })
+        1 * target.output(testId, { TestOutputEvent event ->
+            event.destination == TestOutputEvent.Destination.StdErr && event.message == 'this is stderr' })
+
+        when:
+        processor.completed(testId, completeEvent)
+
+        then:
+        1 * redirector.stop()
+        1 * target.completed(testId, completeEvent)
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessorTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessorTest.groovy
index 4a93907..1e119cd 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessorTest.groovy
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/MaxNParallelTestClassProcessorTest.groovy
@@ -16,16 +16,16 @@
 
 package org.gradle.api.internal.tasks.testing.processors
 
-import spock.lang.Specification
-import org.gradle.api.internal.tasks.testing.TestClassProcessorFactory
-import org.gradle.api.internal.tasks.testing.TestResultProcessor
-import org.gradle.api.internal.tasks.testing.TestClassRunInfo
 import org.gradle.api.internal.tasks.testing.TestClassProcessor
-import org.gradle.messaging.actor.ActorFactory
+import org.gradle.api.internal.tasks.testing.TestClassRunInfo
+import org.gradle.api.internal.tasks.testing.TestResultProcessor
 import org.gradle.messaging.actor.Actor
+import org.gradle.messaging.actor.ActorFactory
+import spock.lang.Specification
+import org.gradle.api.internal.Factory
 
 class MaxNParallelTestClassProcessorTest extends Specification {
-    private final TestClassProcessorFactory factory = Mock()
+    private final Factory<TestClassProcessor> factory = Mock()
     private final TestResultProcessor resultProcessor = Mock()
     private final TestResultProcessor asyncResultProcessor = Mock()
     private final Actor resultProcessorActor = Mock()
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessorTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessorTest.java
index 188645e..e901786 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessorTest.java
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/processors/RestartEveryNTestClassProcessorTest.java
@@ -16,10 +16,10 @@
 
 package org.gradle.api.internal.tasks.testing.processors;
 
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
-import org.gradle.api.internal.tasks.testing.TestClassProcessorFactory;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -29,7 +29,7 @@ import org.junit.runner.RunWith;
 @RunWith(JMock.class)
 public class RestartEveryNTestClassProcessorTest {
     private final JUnit4Mockery context = new JUnit4Mockery();
-    private final TestClassProcessorFactory factory = context.mock(TestClassProcessorFactory.class);
+    private final Factory<TestClassProcessor> factory = context.mock(Factory.class);
     private final TestClassProcessor delegate = context.mock(TestClassProcessor.class);
     private final TestClassRunInfo test1 = context.mock(TestClassRunInfo.class, "test1");
     private final TestClassRunInfo test2 = context.mock(TestClassRunInfo.class, "test2");
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy
index 18ad08f..30d4f2c 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/results/TestLoggerTest.groovy
@@ -23,32 +23,34 @@ import org.gradle.logging.ProgressLoggerFactory
 import org.gradle.logging.ProgressLogger
 import org.gradle.api.tasks.testing.TestDescriptor
 import org.gradle.api.tasks.testing.TestResult
+import org.slf4j.Logger
 
 class TestLoggerTest extends Specification {
     private final ProgressLoggerFactory factory = Mock()
     private final ProgressLogger progressLogger = Mock()
     private final TestDescriptor rootSuite = suite(true)
-    private final TestLogger logger = new TestLogger(factory)
+    private final Logger errorLogger = Mock()
+    private final TestLogger logger = new TestLogger(factory, errorLogger)
 
     def startsProgressLoggerWhenRootSuiteIsStartedAndStopsWhenRootSuiteIsCompleted() {
         when:
         logger.beforeSuite(rootSuite)
 
         then:
-        1 * factory.start() >> progressLogger
+        1 * factory.start(TestLogger.name) >> progressLogger
 
         when:
         logger.afterSuite(rootSuite, result())
 
         then:
-        1 * progressLogger.completed('')
+        1 * progressLogger.completed()
     }
 
     def logsCountOfTestsExecuted() {
         TestDescriptor test1 = test()
         TestDescriptor test2 = test()
 
-        1 * factory.start() >> progressLogger
+        1 * factory.start(TestLogger.name) >> progressLogger
         logger.beforeSuite(rootSuite)
 
         when:
@@ -67,14 +69,14 @@ class TestLoggerTest extends Specification {
         logger.afterSuite(rootSuite, result())
 
         then:
-        1 * progressLogger.completed('')
+        1 * progressLogger.completed()
     }
 
     def logsCountOfFailedTests() {
         TestDescriptor test1 = test()
         TestDescriptor test2 = test()
 
-        1 * factory.start() >> progressLogger
+        1 * factory.start(TestLogger.name) >> progressLogger
         logger.beforeSuite(rootSuite)
 
         when:
@@ -93,13 +95,14 @@ class TestLoggerTest extends Specification {
         logger.afterSuite(rootSuite, result())
 
         then:
-        1 * progressLogger.completed('2 tests completed, 1 failure')
+        1 * errorLogger.error('2 tests completed, 1 failure')
+        1 * progressLogger.completed()
     }
 
     def ignoresSuitesOtherThanTheRootSuite() {
         TestDescriptor suite = suite()
 
-        1 * factory.start() >> progressLogger
+        1 * factory.start(TestLogger.name) >> progressLogger
         logger.beforeSuite(rootSuite)
 
         when:
@@ -113,7 +116,7 @@ class TestLoggerTest extends Specification {
         logger.afterSuite(rootSuite, result())
 
         then:
-        1 * progressLogger.completed('')
+        1 * progressLogger.completed()
     }
 
     private def test() {
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy
index 26d38a1..2569caa 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestClassProcessorTest.groovy
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-
-
-
 package org.gradle.api.internal.tasks.testing.testng
 
 import org.gradle.api.GradleException
@@ -44,6 +41,7 @@ import org.junit.Ignore
 import org.gradle.api.internal.tasks.testing.TestCompleteEvent
 import org.gradle.api.internal.tasks.testing.TestStartEvent
 import org.gradle.logging.StandardOutputRedirector
+import org.testng.annotations.AfterMethod
 
 @RunWith(JMock.class)
 class TestNGTestClassProcessorTest {
@@ -138,14 +136,6 @@ class TestNGTestClassProcessorTest {
         processor.stop();
     }
 
-    @Test @Ignore
-    public void executesATestClassWithBrokenConstructor() {
-        processor.startProcessing(resultProcessor);
-        processor.processTestClass(testClass(ATestNGClassWithBrokenConstructor.class));
-        processor.stop();
-        fail()
-    }
-
     @Test
     public void executesATestClassWithBrokenSetup() {
         context.checking {
@@ -199,6 +189,58 @@ class TestNGTestClassProcessorTest {
     }
 
     @Test
+    public void executesATestClassWithDependencyMethod() {
+        context.checking {
+            Sequence sequence = context.sequence('seq')
+
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal suite ->
+                assertThat(suite.name, equalTo('Gradle test'))
+                assertThat(suite.className, nullValue())
+            }
+            inSequence(sequence)
+
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test ->
+                assertThat(test.name, equalTo('beforeMethod'))
+                assertThat(test.className, equalTo(ATestNGClassWithBrokenDependencyMethod.class.name))
+            }
+            inSequence(sequence)
+
+            one(resultProcessor).failure(2L, ATestNGClassWithBrokenDependencyMethod.failure)
+
+            one(resultProcessor).completed(withParam(equalTo(2L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, equalTo(ResultType.FAILURE))
+            }
+            inSequence(sequence)
+
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test ->
+                assertThat(test.name, equalTo('test'))
+                assertThat(test.className, equalTo(ATestNGClassWithBrokenDependencyMethod.class.name))
+            }
+            inSequence(sequence)
+
+            one(resultProcessor).completed(withParam(equalTo(3L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, equalTo(ResultType.SKIPPED))
+            }
+            inSequence(sequence)
+
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, nullValue())
+            }
+            inSequence(sequence)
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(ATestNGClassWithBrokenDependencyMethod.class));
+        processor.stop();
+    }
+
+    @Test
     public void canIncludeAndExcludeGroups() {
         context.checking {
             one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
@@ -226,6 +268,32 @@ class TestNGTestClassProcessorTest {
         processor.stop();
     }
 
+    @Test @Ignore
+    public void executesATestClassWithBrokenConstructor() {
+        context.checking {
+            Sequence sequence = context.sequence('seq')
+
+            one(resultProcessor).started(withParam(notNullValue()), withParam(notNullValue()))
+            will { TestDescriptorInternal test ->
+                assertThat(test.name, equalTo('initializationError'))
+                assertThat(test.className, equalTo(ATestNGClassWithBrokenConstructor.class.name))
+            }
+            inSequence(sequence)
+
+            one(resultProcessor).failure(1L, ATestNGClassWithBrokenConstructor.failure)
+
+            one(resultProcessor).completed(withParam(equalTo(1L)), withParam(notNullValue()))
+            will { id, TestCompleteEvent event ->
+                assertThat(event.resultType, equalTo(ResultType.FAILURE))
+            }
+            inSequence(sequence)
+        }
+
+        processor.startProcessing(resultProcessor);
+        processor.processTestClass(testClass(ATestNGClassWithBrokenConstructor.class));
+        processor.stop();
+    }
+
     @Test
     public void failsEarlyForUnknownTestClass() {
         processor.startProcessing(resultProcessor)
@@ -264,9 +332,17 @@ public class ATestNGClass {
     public void beforeMethod() {
     }
 
+    @AfterMethod
+    public void afterMethod() {
+    }
+
     @org.testng.annotations.Test
     public void ok() {
     }
+
+    @org.testng.annotations.Test(enabled = false)
+    public void skipped() {
+    }
 }
 
 public class ATestNGClassWithExpectedException {
@@ -294,14 +370,14 @@ public class ATestNGClassWithGroups {
     }
 }
 
-public static class ATestNGFactoryClass {
+public class ATestNGFactoryClass {
     @Factory
     public Object[] suite() {
         return [new ATestNGClass()] as Object[]
     }
 }
 
-public static class ATestNGClassWithBrokenConstructor {
+public class ATestNGClassWithBrokenConstructor {
     static RuntimeException failure = new RuntimeException()
 
     def ATestNGClassWithBrokenConstructor() {
@@ -313,7 +389,7 @@ public static class ATestNGClassWithBrokenConstructor {
     }
 }
 
-public static class ATestNGClassWithBrokenSetupMethod {
+public class ATestNGClassWithBrokenSetupMethod {
     static RuntimeException failure = new RuntimeException()
 
     @BeforeMethod
@@ -323,6 +399,18 @@ public static class ATestNGClassWithBrokenSetupMethod {
 
     @org.testng.annotations.Test
     public void test() {
-        System.out.println("EXECUTE");
+    }
+}
+
+public class ATestNGClassWithBrokenDependencyMethod {
+    static RuntimeException failure = new RuntimeException()
+
+    @org.testng.annotations.Test
+    public void beforeMethod() {
+        throw failure
+    }
+
+    @org.testng.annotations.Test(dependsOnMethods = 'beforeMethod')
+    public void test() {
     }
 }
\ No newline at end of file
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.java
index cc03409..6db4a91 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.java
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.java
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.tasks.testing.worker;
 
 import org.gradle.api.Action;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
@@ -25,7 +26,6 @@ import org.gradle.process.JavaForkOptions;
 import org.gradle.process.internal.JavaExecHandleBuilder;
 import org.gradle.process.internal.WorkerProcess;
 import org.gradle.process.internal.WorkerProcessBuilder;
-import org.gradle.process.internal.WorkerProcessFactory;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JMock;
 import org.jmock.integration.junit4.JUnit4Mockery;
@@ -36,8 +36,8 @@ import org.junit.runner.RunWith;
 import java.io.File;
 import java.util.List;
 
-import static java.util.Arrays.*;
-import static org.hamcrest.Matchers.*;
+import static java.util.Arrays.asList;
+import static org.hamcrest.Matchers.notNullValue;
 
 @RunWith(JMock.class)
 public class ForkingTestClassProcessorTest {
@@ -45,7 +45,7 @@ public class ForkingTestClassProcessorTest {
         setImposteriser(ClassImposteriser.INSTANCE);
     }};
     private final WorkerTestClassProcessorFactory processorFactory = context.mock(WorkerTestClassProcessorFactory.class);
-    private final WorkerProcessFactory workerFactory = context.mock(WorkerProcessFactory.class);
+    private final Factory<WorkerProcessBuilder> workerFactory = context.mock(Factory.class);
     private final WorkerProcess workerProcess = context.mock(WorkerProcess.class);
     private final RemoteTestClassProcessor worker = context.mock(RemoteTestClassProcessor.class);
     private final TestClassRunInfo test1 = context.mock(TestClassRunInfo.class, "test1");
@@ -106,7 +106,7 @@ public class ForkingTestClassProcessorTest {
             ObjectConnection connection = context.mock(ObjectConnection.class);
             JavaExecHandleBuilder javaCommandBuilder = context.mock(JavaExecHandleBuilder.class);
 
-            one(workerFactory).newProcess();
+            one(workerFactory).create();
             will(returnValue(builder));
 
             one(builder).worker(with(notNullValue(TestWorker.class)));
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
index 37189f4..08c2724 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
@@ -93,8 +93,10 @@ class JavaBasePluginTest extends Specification {
         then:
         task.sourceCompatibility == project.sourceCompatibility.toString()
 
-        task = project.createTask('customTest', type: org.gradle.api.tasks.testing.Test)
+        task = project.createTask('customTest', type: Test.class)
         task.workingDir == project.projectDir
+        task.testResultsDir == project.testResultsDir
+        task.testReportDir == project.testReportDir
 
         task = project.createTask('customJavadoc', type: Javadoc)
         task.destinationDir == project.file("$project.docsDir/javadoc")
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
index fb2d930..33203f2 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
@@ -119,7 +119,7 @@ class JavaPluginTest {
         assertThat(set.runtimeClasspath.sourceCollections, hasItem(project.configurations.runtime))
         assertThat(set.runtimeClasspath, hasItem(new File(project.buildDir, 'classes/custom')))
     }
-    
+
     @Test public void createsStandardTasksAndAppliesMappings() {
         javaPlugin.apply(project)
 
@@ -209,6 +209,17 @@ class JavaPluginTest {
         assertThat(task, dependsOn(JavaBasePlugin.BUILD_TASK_NAME))
     }
 
+    @Test public void appliesMappingsToTasksAddedByTheBuildScript() {
+        javaPlugin.apply(project);
+
+        def task = project.createTask('customTest', type: org.gradle.api.tasks.testing.Test.class)
+        assertThat(task.classpath, equalTo(project.sourceSets.test.runtimeClasspath))
+        assertThat(task.testClassesDir, equalTo(project.sourceSets.test.classesDir))
+        assertThat(task.workingDir, equalTo(project.projectDir))
+        assertThat(task.testResultsDir, equalTo(project.testResultsDir))
+        assertThat(task.testReportDir, equalTo(project.testReportDir))
+    }
+
     @Test public void buildOtherProjects() {
         DefaultProject commonProject = HelperUtil.createChildProject(project, "common");
         DefaultProject middleProject = HelperUtil.createChildProject(project, "middle");
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
index 8274065..fea1308 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/api/tasks/javadoc/JavadocTest.java
@@ -21,7 +21,7 @@ import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.internal.file.SimpleFileCollection;
 import org.gradle.api.tasks.AbstractConventionTaskTest;
-import org.gradle.external.javadoc.JavadocExecHandleBuilder;
+import org.gradle.external.javadoc.internal.JavadocExecHandleBuilder;
 import org.gradle.external.javadoc.StandardJavadocDocletOptions;
 import org.gradle.process.internal.ExecAction;
 import org.gradle.process.internal.ExecException;
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/JavadocExecHandleBuilderTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/JavadocExecHandleBuilderTest.groovy
deleted file mode 100644
index 240ae70..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/JavadocExecHandleBuilderTest.groovy
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc;
-
-
-import org.gradle.util.Jvm
-import org.junit.Test
-import spock.lang.Specification
-import static org.junit.Assert.assertTrue
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocExecHandleBuilderTest extends Specification {
-
-    private JavadocExecHandleBuilder javadocExecHandleBuilder = new JavadocExecHandleBuilder()
-
-    def setup() {
-        MinimalJavadocOptions minimalJavadocOptions = Mock()
-        javadocExecHandleBuilder.options = minimalJavadocOptions
-        javadocExecHandleBuilder.optionsFile = new File(".")
-    }
-
-    def testCheckExecutableAfterInit() {
-        expect:
-        javadocExecHandleBuilder.execHandle.executable == Jvm.current().javadocExecutable.absolutePath
-    }
-
-    def testCheckCustomExecutable() {
-        String executable = "somepath"
-        javadocExecHandleBuilder.executable = executable
-        
-        expect:
-        javadocExecHandleBuilder.execHandle.executable == executable
-    }
-
-    def testSetNullExecDirectory() {
-        when:
-        javadocExecHandleBuilder.execDirectory(null)
-
-        then:
-        thrown(java.lang.IllegalArgumentException)
-    }
-
-    def testSetNotExistingDirectory() {
-        when:
-        javadocExecHandleBuilder.execDirectory(new File(".notExistingTestDirectoryX"));
-
-        then:
-        thrown(java.lang.IllegalArgumentException)
-    }
-
-    def testSetExistingExecDirectory() {
-        File existingDirectory = new File(".existingDirectory");
-        assertTrue(existingDirectory.mkdir());
-
-        expect:
-        try {
-            javadocExecHandleBuilder.execDirectory(existingDirectory);
-        }
-        finally {
-            existingDirectory.delete()
-        }
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testSetNullOptions() {
-        when:
-        javadocExecHandleBuilder.options(null);
-
-        then:
-        thrown(java.lang.IllegalArgumentException)
-    }
-
-    @Test
-    public void testSetNotNullOptions() {
-        MinimalJavadocOptions options = Mock()
-
-        when:
-        javadocExecHandleBuilder.options(options)
-
-        then:
-        true // no error
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
index 78fbe97..b62a7dc 100644
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/StandardJavadocDocletOptionsTest.java
@@ -16,6 +16,7 @@
 
 package org.gradle.external.javadoc;
 
+import org.gradle.external.javadoc.internal.LinksOfflineJavadocOptionFileOption;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.After;
@@ -23,9 +24,8 @@ import static org.junit.Assert.*;
 import org.jmock.integration.junit4.JUnit4Mockery;
 import org.jmock.lib.legacy.ClassImposteriser;
 import org.jmock.Expectations;
-import org.gradle.external.javadoc.optionfile.JavadocOptionFile;
-import org.gradle.external.javadoc.optionfile.LinksOfflineJavadocOptionFileOption;
-import org.gradle.external.javadoc.optionfile.GroupsJavadocOptionFileOption;
+import org.gradle.external.javadoc.internal.JavadocOptionFile;
+import org.gradle.external.javadoc.internal.GroupsJavadocOptionFileOption;
 
 import java.util.*;
 import java.io.File;
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..0d4ee00
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/BooleanJavadocOptionFileOptionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class BooleanJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+    private BooleanJavadocOptionFileOption booleanOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        booleanOption = new BooleanJavadocOptionFileOption(optionName);
+    }
+
+    @Test
+    public void testWriteNullValue() throws IOException {
+        booleanOption.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteFalseValue() throws IOException {
+        booleanOption.setValue(false);
+
+        booleanOption.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteTrueValue() throws IOException {
+        booleanOption.setValue(true);
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeOption(optionName);
+        }});
+
+        booleanOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..1388066
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/EnumJavadocOptionFileOptionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+import org.gradle.external.javadoc.JavadocMemberLevel;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class EnumJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+    private EnumJavadocOptionFileOption<JavadocMemberLevel> enumOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        enumOption = new EnumJavadocOptionFileOption<JavadocMemberLevel>(optionName);
+    }
+
+    @Test
+    public void testWriteNullValue() throws IOException {
+        enumOption.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteNoneNullValue() throws IOException {
+        enumOption.setValue(JavadocMemberLevel.PUBLIC);
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeOption("public");
+        }});
+
+        enumOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..1916bcb
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/FileJavadocOptionFileOptionTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class FileJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+    private FileJavadocOptionFileOption fileOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        fileOption = new FileJavadocOptionFileOption(optionName);
+    }
+
+    @Test
+    public void testWriteNullValue() throws IOException {
+        fileOption.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteNoneNullValue() throws IOException {
+        final File testValue = new File("dummyTestFileValue");
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeValueOption(optionName, testValue.getAbsolutePath());
+        }});
+        
+        fileOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..e2dae37
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/GroupsJavadocOptionFileOptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class GroupsJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+
+    private GroupsJavadocOptionFileOption groupsFile;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        groupsFile = new GroupsJavadocOptionFileOption(optionName);
+    }
+
+    @Test
+    public void testWriteNullValue() throws IOException {
+        groupsFile.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteNotNullValue() throws IOException {
+        final String groupName = "testGroup";
+        final List<String> groupElements = Arrays.asList("java.lang", "java.util*");
+
+        groupsFile.getValue().put(groupName, groupElements);
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeOptionHeader(optionName);
+            one(writerContextMock).write("\"testGroup\"");
+            one(writerContextMock).write(" ");
+            one(writerContextMock).write("\"java.lang:java.util*\"");
+            one(writerContextMock).newLine();
+        }});
+
+        groupsFile.write(writerContextMock);
+    }
+
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy
new file mode 100644
index 0000000..8e37f4b
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocExecHandleBuilderTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+
+import org.gradle.util.Jvm
+import org.junit.Test
+import spock.lang.Specification
+import static org.junit.Assert.assertTrue
+import org.gradle.external.javadoc.MinimalJavadocOptions
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocExecHandleBuilderTest extends Specification {
+
+    private JavadocExecHandleBuilder javadocExecHandleBuilder = new JavadocExecHandleBuilder()
+
+    def setup() {
+        MinimalJavadocOptions minimalJavadocOptions = Mock()
+        javadocExecHandleBuilder.options = minimalJavadocOptions
+        javadocExecHandleBuilder.optionsFile = new File(".")
+    }
+
+    def testCheckExecutableAfterInit() {
+        expect:
+        javadocExecHandleBuilder.execHandle.executable == Jvm.current().javadocExecutable.absolutePath
+    }
+
+    def testCheckCustomExecutable() {
+        String executable = "somepath"
+        javadocExecHandleBuilder.executable = executable
+        
+        expect:
+        javadocExecHandleBuilder.execHandle.executable == executable
+    }
+
+    def testSetNullExecDirectory() {
+        when:
+        javadocExecHandleBuilder.execDirectory(null)
+
+        then:
+        thrown(java.lang.IllegalArgumentException)
+    }
+
+    def testSetNotExistingDirectory() {
+        when:
+        javadocExecHandleBuilder.execDirectory(new File(".notExistingTestDirectoryX"));
+
+        then:
+        thrown(java.lang.IllegalArgumentException)
+    }
+
+    def testSetExistingExecDirectory() {
+        File existingDirectory = new File(".existingDirectory");
+        assertTrue(existingDirectory.mkdir());
+
+        expect:
+        try {
+            javadocExecHandleBuilder.execDirectory(existingDirectory);
+        }
+        finally {
+            existingDirectory.delete()
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetNullOptions() {
+        when:
+        javadocExecHandleBuilder.options(null);
+
+        then:
+        thrown(java.lang.IllegalArgumentException)
+    }
+
+    @Test
+    public void testSetNotNullOptions() {
+        MinimalJavadocOptions options = Mock()
+
+        when:
+        javadocExecHandleBuilder.options(options)
+
+        then:
+        true // no error
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java
new file mode 100644
index 0000000..8252717
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.gradle.external.javadoc.JavadocOptionFileOption;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocOptionFileTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileOption optionFileOptionMock;
+    private final String optionName = "testOption";
+    
+
+    private JavadocOptionFile optionFile;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+
+        optionFileOptionMock = context.mock(JavadocOptionFileOption.class);
+
+        optionFile = new JavadocOptionFile();
+    }
+
+    @Test
+    public void testDefaults() {
+        assertNotNull(optionFile.getOptions());
+        assertTrue(optionFile.getOptions().isEmpty());
+
+        assertNotNull(optionFile.getSourceNames());
+        assertNotNull(optionFile.getSourceNames().getValue());
+        assertTrue(optionFile.getSourceNames().getValue().isEmpty());
+    }
+
+    @Test
+    public void testAddOption() {
+        context.checking(new Expectations() {{
+            one(optionFileOptionMock).getOption();
+            returnValue(optionName);
+        }});
+
+        optionFile.addOption(optionFileOptionMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.java
new file mode 100644
index 0000000..097bf81
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/JavadocOptionFileWriterContextTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+import org.gradle.util.WrapUtil;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class JavadocOptionFileWriterContextTest {
+
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private BufferedWriter bufferedWriterMock;
+
+    private JavadocOptionFileWriterContext writerContext;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        bufferedWriterMock = context.mock(BufferedWriter.class);
+
+        writerContext = new JavadocOptionFileWriterContext(bufferedWriterMock);
+    }
+
+    @Test
+    public void testWrite() throws IOException {
+        final String writeValue = "dummy";
+
+        context.checking(new Expectations() {{
+            one(bufferedWriterMock).write(writeValue);
+        }});
+
+        writerContext.write(writeValue);
+    }
+
+    @Test
+    public void testNewLine() throws IOException {
+        context.checking(new Expectations() {{
+            one(bufferedWriterMock).newLine();
+        }});
+
+        writerContext.newLine();
+    }
+
+    @Test
+    public void quotesAndEscapesOptionValue() throws IOException {
+        context.checking(new Expectations(){{
+            one(bufferedWriterMock).write("-");
+            one(bufferedWriterMock).write("key");
+            one(bufferedWriterMock).write(" ");
+            one(bufferedWriterMock).write("'");
+            one(bufferedWriterMock).write("1\\\\2\\\\");
+            one(bufferedWriterMock).write("'");
+            one(bufferedWriterMock).newLine();
+        }});
+
+        writerContext.writeValueOption("key", "1\\2\\");
+    }
+
+    @Test
+    public void quotesAndEscapesOptionValues() throws IOException {
+        context.checking(new Expectations(){{
+            one(bufferedWriterMock).write("-");
+            one(bufferedWriterMock).write("key");
+            one(bufferedWriterMock).write(" ");
+            one(bufferedWriterMock).write("'");
+            one(bufferedWriterMock).write("a\\\\b:c");
+            one(bufferedWriterMock).write("'");
+            one(bufferedWriterMock).newLine();
+        }});
+
+        writerContext.writeValuesOption("key", WrapUtil.toList("a\\b", "c"), ":");
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..36ea5e5
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/LinksOfflineJavadocOptionFileOptionTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+import org.gradle.external.javadoc.JavadocOfflineLink;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class LinksOfflineJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+
+    private LinksOfflineJavadocOptionFileOption linksOfflineOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        linksOfflineOption = new LinksOfflineJavadocOptionFileOption(optionName);
+    }
+
+    @Test
+    public void writeNullValue() throws IOException {
+        linksOfflineOption.write(writerContextMock);
+    }
+
+    @Test
+    public void writeNoneNullValue() throws IOException {
+        final String extDocUrl = "extDocUrl";
+        final String packageListLoc = "packageListLoc";
+
+        linksOfflineOption.getValue().add(new JavadocOfflineLink(extDocUrl, packageListLoc));
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeValueOption(optionName, extDocUrl + "' '" + packageListLoc);
+        }});
+
+        linksOfflineOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..51a6b3b
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/MultilineStringsJavadocOptionFileOptionTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Melanie Pfautz
+ */
+public class MultilineStringsJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+
+    private MultilineStringsJavadocOptionFileOption linksOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+        linksOption = new MultilineStringsJavadocOptionFileOption(optionName);
+    }
+
+    @Test
+    public void writeNullValue() throws IOException {
+        linksOption.writeCollectionValue(writerContextMock);
+    }
+
+    @Test
+    public void writeNonNullValue() throws IOException {
+        final String extDocUrl = "extDocUrl";
+
+        linksOption.getValue().add(extDocUrl);
+        context.checking(new Expectations() {{
+            final List<String> tempList = new ArrayList<String>();
+            tempList.add(extDocUrl);
+            one(writerContextMock).writeMultilineValuesOption(optionName, tempList);
+        }});
+
+        linksOption.writeCollectionValue(writerContextMock);
+    }
+
+    @Test
+    public void writeMultipleValues() throws IOException {
+        final List<String> tempList = new ArrayList<String>();
+        final String docUrl1 = "docUrl1";
+        final String docUrl2 = "docUrl2";
+
+        linksOption.getValue().add(docUrl1);
+        linksOption.getValue().add(docUrl2);
+        context.checking(new Expectations() {{
+            tempList.add(docUrl1);
+            tempList.add(docUrl2);
+            one(writerContextMock).writeMultilineValuesOption(optionName, tempList);
+        }});
+       
+        linksOption.writeCollectionValue(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..eac4d65
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/OptionLessStringsJavadocOptionFileOptionTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class OptionLessStringsJavadocOptionFileOptionTest {
+
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+
+    private OptionLessStringsJavadocOptionFileOption optionLessStringsOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        optionLessStringsOption = new OptionLessStringsJavadocOptionFileOption();
+    }
+
+    @Test
+    public void writeNullValue() throws IOException {
+        final String firstValue = "firstValue";
+        final String secondValue = "secondValue";
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).write(firstValue);
+            one(writerContextMock).newLine();
+            one(writerContextMock).write(secondValue);
+            one(writerContextMock).newLine();
+        }});
+
+        optionLessStringsOption.write(writerContextMock);
+    }
+
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..35238bd
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/PathJavadocOptionFileOptionTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class PathJavadocOptionFileOptionTest {
+
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+    private final String joinBy = ";";
+
+    private PathJavadocOptionFileOption pathOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        pathOption = new PathJavadocOptionFileOption(optionName, joinBy);
+    }
+
+    @Test
+    public void testWriteNullValue() throws IOException {
+        pathOption.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteNoneNullValue() throws IOException {
+        final File fileOne = new File("fileOne");
+        final File fileTwo = new File("fileTwo");
+
+        pathOption.getValue().add(fileOne);
+        pathOption.getValue().add(fileTwo);
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writePathOption(optionName, pathOption.getValue(), joinBy);
+        }});
+
+        pathOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..65c64b6
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringJavadocOptionFileOptionTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class StringJavadocOptionFileOptionTest {
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+
+    private StringJavadocOptionFileOption stringOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        stringOption = new StringJavadocOptionFileOption(optionName);
+    }
+
+    @Test
+    public void testWriteNullValue() throws IOException {
+        stringOption.write(writerContextMock);
+    }
+
+    @Test
+    public void testWriteNoneNullValue() throws IOException {
+        final String testValue = "testValue";
+
+        stringOption.setValue(testValue);
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeValueOption(optionName, testValue);
+        }});
+
+        stringOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java
new file mode 100644
index 0000000..e5e58a2
--- /dev/null
+++ b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/internal/StringsJavadocOptionFileOptionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.external.javadoc.internal;
+
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.jmock.Expectations;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+/**
+ * @author Tom Eyckmans
+ */
+public class StringsJavadocOptionFileOptionTest {
+
+    private final JUnit4Mockery context = new JUnit4Mockery();
+    private JavadocOptionFileWriterContext writerContextMock;
+    private final String optionName = "testOption";
+    private final String joinBy = ";";
+
+    private StringsJavadocOptionFileOption stringsOption;
+
+    @Before
+    public void setUp() {
+        context.setImposteriser(ClassImposteriser.INSTANCE);
+        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
+
+        stringsOption = new StringsJavadocOptionFileOption(optionName, joinBy);
+    }
+
+    @Test
+    public void writeNullValue() throws IOException {
+        stringsOption.write(writerContextMock);
+    }
+
+    @Test
+    public void writeNoneNullValue() throws IOException {
+        final String valueOne = "valueOne";
+        final String valueTwo = "valueTwo";
+
+        stringsOption.getValue().add(valueOne);
+        stringsOption.getValue().add(valueTwo);
+
+        context.checking(new Expectations() {{
+            one(writerContextMock).writeValuesOption(optionName, stringsOption.getValue(), joinBy);
+        }});
+
+        stringsOption.write(writerContextMock);
+    }
+}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/BooleanJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/BooleanJavadocOptionFileOptionTest.java
deleted file mode 100644
index 84e202d..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/BooleanJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class BooleanJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-    private BooleanJavadocOptionFileOption booleanOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        booleanOption = new BooleanJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void testWriteNullValue() throws IOException {
-        booleanOption.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteFalseValue() throws IOException {
-        booleanOption.setValue(false);
-
-        booleanOption.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteTrueValue() throws IOException {
-        booleanOption.setValue(true);
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeOption(optionName);
-        }});
-
-        booleanOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/EnumJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/EnumJavadocOptionFileOptionTest.java
deleted file mode 100644
index 8ea1574..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/EnumJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-import org.gradle.external.javadoc.JavadocMemberLevel;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class EnumJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-    private EnumJavadocOptionFileOption<JavadocMemberLevel> enumOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        enumOption = new EnumJavadocOptionFileOption<JavadocMemberLevel>(optionName);
-    }
-
-    @Test
-    public void testWriteNullValue() throws IOException {
-        enumOption.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteNoneNullValue() throws IOException {
-        enumOption.setValue(JavadocMemberLevel.PUBLIC);
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeOption("public");
-        }});
-
-        enumOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/FileJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/FileJavadocOptionFileOptionTest.java
deleted file mode 100644
index 7fa88b0..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/FileJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.File;
-
-/**
- * @author Tom Eyckmans
- */
-public class FileJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-    private FileJavadocOptionFileOption fileOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        fileOption = new FileJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void testWriteNullValue() throws IOException {
-        fileOption.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteNoneNullValue() throws IOException {
-        final File testValue = new File("dummyTestFileValue");
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeValueOption(optionName, testValue.getAbsolutePath());
-        }});
-        
-        fileOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/GroupsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/GroupsJavadocOptionFileOptionTest.java
deleted file mode 100644
index 95a042f..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/GroupsJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Arrays;
-
-/**
- * @author Tom Eyckmans
- */
-public class GroupsJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-
-    private GroupsJavadocOptionFileOption groupsFile;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        groupsFile = new GroupsJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void testWriteNullValue() throws IOException {
-        groupsFile.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteNotNullValue() throws IOException {
-        final String groupName = "testGroup";
-        final List<String> groupElements = Arrays.asList("java.lang", "java.util*");
-
-        groupsFile.getValue().put(groupName, groupElements);
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeOptionHeader(optionName);
-            one(writerContextMock).write("\"testGroup\"");
-            one(writerContextMock).write(" ");
-            one(writerContextMock).write("\"java.lang:java.util*\"");
-            one(writerContextMock).newLine();
-        }});
-
-        groupsFile.write(writerContextMock);
-    }
-
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileTest.java
deleted file mode 100644
index 7b5f0f3..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocOptionFileTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileOption optionFileOptionMock;
-    private final String optionName = "testOption";
-    
-
-    private JavadocOptionFile optionFile;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-
-        optionFileOptionMock = context.mock(JavadocOptionFileOption.class);
-
-        optionFile = new JavadocOptionFile();
-    }
-
-    @Test
-    public void testDefaults() {
-        assertNotNull(optionFile.getOptions());
-        assertTrue(optionFile.getOptions().isEmpty());
-
-        assertNotNull(optionFile.getSourceNames());
-        assertNotNull(optionFile.getSourceNames().getValue());
-        assertTrue(optionFile.getSourceNames().getValue().isEmpty());
-    }
-
-    @Test
-    public void testAddOption() {
-        context.checking(new Expectations() {{
-            one(optionFileOptionMock).getOption();
-            returnValue(optionName);
-        }});
-
-        optionFile.addOption(optionFileOptionMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriterContextTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriterContextTest.java
deleted file mode 100644
index 566adb0..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/JavadocOptionFileWriterContextTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-import org.gradle.util.WrapUtil;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class JavadocOptionFileWriterContextTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private BufferedWriter bufferedWriterMock;
-
-    private JavadocOptionFileWriterContext writerContext;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        bufferedWriterMock = context.mock(BufferedWriter.class);
-
-        writerContext = new JavadocOptionFileWriterContext(bufferedWriterMock);
-    }
-
-    @Test
-    public void testWrite() throws IOException {
-        final String writeValue = "dummy";
-
-        context.checking(new Expectations() {{
-            one(bufferedWriterMock).write(writeValue);
-        }});
-
-        writerContext.write(writeValue);
-    }
-
-    @Test
-    public void testNewLine() throws IOException {
-        context.checking(new Expectations() {{
-            one(bufferedWriterMock).newLine();
-        }});
-
-        writerContext.newLine();
-    }
-
-    @Test
-    public void quotesAndEscapesOptionValue() throws IOException {
-        context.checking(new Expectations(){{
-            one(bufferedWriterMock).write("-");
-            one(bufferedWriterMock).write("key");
-            one(bufferedWriterMock).write(" ");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).write("1\\\\2\\\\");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).newLine();
-        }});
-
-        writerContext.writeValueOption("key", "1\\2\\");
-    }
-
-    @Test
-    public void quotesAndEscapesOptionValues() throws IOException {
-        context.checking(new Expectations(){{
-            one(bufferedWriterMock).write("-");
-            one(bufferedWriterMock).write("key");
-            one(bufferedWriterMock).write(" ");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).write("a\\\\b:c");
-            one(bufferedWriterMock).write("'");
-            one(bufferedWriterMock).newLine();
-        }});
-
-        writerContext.writeValuesOption("key", WrapUtil.toList("a\\b", "c"), ":");
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/LinksOfflineJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/LinksOfflineJavadocOptionFileOptionTest.java
deleted file mode 100644
index 4a6664f..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/LinksOfflineJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-import org.gradle.external.javadoc.JavadocOfflineLink;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class LinksOfflineJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-
-    private LinksOfflineJavadocOptionFileOption linksOfflineOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        linksOfflineOption = new LinksOfflineJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void writeNullValue() throws IOException {
-        linksOfflineOption.write(writerContextMock);
-    }
-
-    @Test
-    public void writeNoneNullValue() throws IOException {
-        final String extDocUrl = "extDocUrl";
-        final String packageListLoc = "packageListLoc";
-
-        linksOfflineOption.getValue().add(new JavadocOfflineLink(extDocUrl, packageListLoc));
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeValueOption(optionName, extDocUrl + "' '" + packageListLoc);
-        }});
-
-        linksOfflineOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/MultilineStringsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/MultilineStringsJavadocOptionFileOptionTest.java
deleted file mode 100644
index 58451c9..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/MultilineStringsJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author Melanie Pfautz
- */
-public class MultilineStringsJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-
-    private MultilineStringsJavadocOptionFileOption linksOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-        linksOption = new MultilineStringsJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void writeNullValue() throws IOException {
-        linksOption.writeCollectionValue(writerContextMock);
-    }
-
-    @Test
-    public void writeNonNullValue() throws IOException {
-        final String extDocUrl = "extDocUrl";
-
-        linksOption.getValue().add(extDocUrl);
-        context.checking(new Expectations() {{
-            final List<String> tempList = new ArrayList<String>();
-            tempList.add(extDocUrl);
-            one(writerContextMock).writeMultilineValuesOption(optionName, tempList);
-        }});
-
-        linksOption.writeCollectionValue(writerContextMock);
-    }
-
-    @Test
-    public void writeMultipleValues() throws IOException {
-        final List<String> tempList = new ArrayList<String>();
-        final String docUrl1 = "docUrl1";
-        final String docUrl2 = "docUrl2";
-
-        linksOption.getValue().add(docUrl1);
-        linksOption.getValue().add(docUrl2);
-        context.checking(new Expectations() {{
-            tempList.add(docUrl1);
-            tempList.add(docUrl2);
-            one(writerContextMock).writeMultilineValuesOption(optionName, tempList);
-        }});
-       
-        linksOption.writeCollectionValue(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/OptionLessStringsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/OptionLessStringsJavadocOptionFileOptionTest.java
deleted file mode 100644
index 3a2bb15..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/OptionLessStringsJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class OptionLessStringsJavadocOptionFileOptionTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-
-    private OptionLessStringsJavadocOptionFileOption optionLessStringsOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        optionLessStringsOption = new OptionLessStringsJavadocOptionFileOption();
-    }
-
-    @Test
-    public void writeNullValue() throws IOException {
-        final String firstValue = "firstValue";
-        final String secondValue = "secondValue";
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).write(firstValue);
-            one(writerContextMock).newLine();
-            one(writerContextMock).write(secondValue);
-            one(writerContextMock).newLine();
-        }});
-
-        optionLessStringsOption.write(writerContextMock);
-    }
-
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/PathJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/PathJavadocOptionFileOptionTest.java
deleted file mode 100644
index 1da2f6c..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/PathJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.File;
-
-/**
- * @author Tom Eyckmans
- */
-public class PathJavadocOptionFileOptionTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-    private final String joinBy = ";";
-
-    private PathJavadocOptionFileOption pathOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        pathOption = new PathJavadocOptionFileOption(optionName, joinBy);
-    }
-
-    @Test
-    public void testWriteNullValue() throws IOException {
-        pathOption.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteNoneNullValue() throws IOException {
-        final File fileOne = new File("fileOne");
-        final File fileTwo = new File("fileTwo");
-
-        pathOption.getValue().add(fileOne);
-        pathOption.getValue().add(fileTwo);
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writePathOption(optionName, pathOption.getValue(), joinBy);
-        }});
-
-        pathOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/StringJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/StringJavadocOptionFileOptionTest.java
deleted file mode 100644
index 2f7bed2..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/StringJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class StringJavadocOptionFileOptionTest {
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-
-    private StringJavadocOptionFileOption stringOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        stringOption = new StringJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void testWriteNullValue() throws IOException {
-        stringOption.write(writerContextMock);
-    }
-
-    @Test
-    public void testWriteNoneNullValue() throws IOException {
-        final String testValue = "testValue";
-
-        stringOption.setValue(testValue);
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeValueOption(optionName, testValue);
-        }});
-
-        stringOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/StringsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/StringsJavadocOptionFileOptionTest.java
deleted file mode 100644
index b76f371..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/StringsJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class StringsJavadocOptionFileOptionTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-    private final String joinBy = ";";
-
-    private StringsJavadocOptionFileOption stringsOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        stringsOption = new StringsJavadocOptionFileOption(optionName, joinBy);
-    }
-
-    @Test
-    public void writeNullValue() throws IOException {
-        stringsOption.write(writerContextMock);
-    }
-
-    @Test
-    public void writeNoneNullValue() throws IOException {
-        final String valueOne = "valueOne";
-        final String valueTwo = "valueTwo";
-
-        stringsOption.getValue().add(valueOne);
-        stringsOption.getValue().add(valueTwo);
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeValuesOption(optionName, stringsOption.getValue(), joinBy);
-        }});
-
-        stringsOption.write(writerContextMock);
-    }
-}
diff --git a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/TagsJavadocOptionFileOptionTest.java b/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/TagsJavadocOptionFileOptionTest.java
deleted file mode 100644
index 5405755..0000000
--- a/subprojects/gradle-plugins/src/test/groovy/org/gradle/external/javadoc/optionfile/TagsJavadocOptionFileOptionTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.external.javadoc.optionfile;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.jmock.lib.legacy.ClassImposteriser;
-import org.jmock.Expectations;
-
-import java.io.IOException;
-
-/**
- * @author Tom Eyckmans
- */
-public class TagsJavadocOptionFileOptionTest {
-
-    private final JUnit4Mockery context = new JUnit4Mockery();
-    private JavadocOptionFileWriterContext writerContextMock;
-    private final String optionName = "testOption";
-    private final String joinBy = ";";
-    
-    private TagsJavadocOptionFileOption tagsOption;
-
-    @Before
-    public void setUp() {
-        context.setImposteriser(ClassImposteriser.INSTANCE);
-        writerContextMock = context.mock(JavadocOptionFileWriterContext.class);
-
-        tagsOption = new TagsJavadocOptionFileOption(optionName);
-    }
-
-    @Test
-    public void writeNullValue() throws IOException {
-        tagsOption.write(writerContextMock);
-    }
-
-    @Test
-    public void writeNoneNullValue() throws IOException {
-        tagsOption.getValue().add("testTag");
-        tagsOption.getValue().add("testTaglet:testTagletOne");
-        tagsOption.getValue().add("testTaglet\"testTagletTwo");
-
-        context.checking(new Expectations() {{
-            one(writerContextMock).writeValueOption("taglet", "testTag");
-            one(writerContextMock).writeValueOption("tag", "testTaglet:testTagletOne");
-            one(writerContextMock).writeValueOption("tag", "testTaglet\"testTagletTwo");
-        }});
-
-        tagsOption.write(writerContextMock);
-    }
-
-}
diff --git a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy
index 484c175..77f8d61 100644
--- a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy
+++ b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.groovy
@@ -39,7 +39,7 @@ public class ScalaPlugin implements Plugin<Project> {
             scalaDoc.conventionMapping.defaultSource = { project.sourceSets.main.scala }
         }
         ScalaDoc scalaDoc = project.tasks.add(SCALA_DOC_TASK_NAME, ScalaDoc.class)
-        scalaDoc.description = "Generates scaladoc for the source code.";
+        scalaDoc.description = "Generates scaladoc for the main source code.";
         scalaDoc.group = JavaBasePlugin.DOCUMENTATION_GROUP
     }
 }
diff --git a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
index c3f866f..eb6e7c7 100644
--- a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
+++ b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompile.java
@@ -15,9 +15,10 @@
  */
 package org.gradle.api.tasks.scala;
 
+import org.gradle.api.AntBuilder;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
-import org.gradle.api.internal.project.AntBuilderFactory;
+import org.gradle.api.internal.Factory;
 import org.gradle.api.internal.project.IsolatedAntBuilder;
 import org.gradle.api.internal.tasks.compile.AntJavaCompiler;
 import org.gradle.api.internal.tasks.compile.JavaCompiler;
@@ -28,7 +29,7 @@ import org.gradle.api.tasks.compile.AbstractCompile;
 import org.gradle.api.tasks.compile.CompileOptions;
 
 /**
- * Task to perform scala compilation.
+ * Compiles Scala and Java source files.
  */
 public class ScalaCompile extends AbstractCompile {
     private FileCollection scalaClasspath;
@@ -37,7 +38,7 @@ public class ScalaCompile extends AbstractCompile {
 
     public ScalaCompile() {
         ScalaCompiler scalaCompiler = new AntScalaCompiler(getServices().get(IsolatedAntBuilder.class));
-        JavaCompiler javaCompiler = new AntJavaCompiler(getServices().get(AntBuilderFactory.class));
+        JavaCompiler javaCompiler = new AntJavaCompiler((Factory) getServices().getFactory(AntBuilder.class));
         compiler = new IncrementalScalaCompiler(new DefaultScalaJavaJointCompiler(scalaCompiler, javaCompiler), getOutputs());
     }
 
diff --git a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.groovy b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.groovy
index 225928e..5e8d216 100644
--- a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.groovy
+++ b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaCompileOptions.groovy
@@ -136,7 +136,7 @@ public class ScalaCompileOptions extends AbstractOptions {
     }
 
     List excludedFieldsFromOptionMap() {
-        ['useCompileDaemon']
+        ['useCompileDaemon'] + (optimize ? [] : ['optimize'])
     }
 
     private String toOnOffString(value) {
diff --git a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java
index 695a79c..987bb01 100644
--- a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java
+++ b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/ScalaDoc.java
@@ -26,7 +26,7 @@ import org.gradle.util.GUtil;
 import java.io.File;
 
 /**
- * Task to generate Scala API documentation.
+ * Generates HTML API documentation for Scala source files.
  */
 public class ScalaDoc extends SourceTask {
 
diff --git a/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/package-info.java b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/package-info.java
new file mode 100644
index 0000000..0b6744f
--- /dev/null
+++ b/subprojects/gradle-scala/src/main/groovy/org/gradle/api/tasks/scala/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Scala {@link org.gradle.api.Task} implementations.
+ */
+package org.gradle.api.tasks.scala;
diff --git a/subprojects/gradle-scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileOptionsTest.groovy b/subprojects/gradle-scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileOptionsTest.groovy
index 441b685..35fc105 100644
--- a/subprojects/gradle-scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileOptionsTest.groovy
+++ b/subprojects/gradle-scala/src/test/groovy/org/gradle/api/tasks/scala/ScalaCompileOptionsTest.groovy
@@ -58,7 +58,10 @@ public class ScalaCompileOptionsTest {
     }
 
     @Test public void testOptionMapContainsOptimize() {
-        assertOnOffValue('optimize', 'optimise', false)
+        assertFalse(compileOptions.optionMap().containsKey('optimise'))
+
+        compileOptions.optimize = true
+        assertThat(compileOptions.optionMap()['optimise'], equalTo('on'))
     }
 
     @Test public void testOptionMapContainsEncodingIfSpecified() {
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/FavoritesIntegrationTest.java b/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/FavoritesIntegrationTest.java
similarity index 100%
rename from subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/FavoritesIntegrationTest.java
rename to subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/FavoritesIntegrationTest.java
diff --git a/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy b/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
new file mode 100644
index 0000000..fa5287f
--- /dev/null
+++ b/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+package org.gradle.integtests
+
+import org.gradle.foundation.TestUtility
+import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol
+import org.gradle.gradleplugin.foundation.GradlePluginLord
+import org.gradle.gradleplugin.foundation.runner.GradleRunner
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.locks.Lock
+import java.util.concurrent.locks.ReentrantLock
+import java.util.concurrent.locks.Condition
+
+/**
+This tests the that live output is gathered while executing a task.
+ at author mhunsicker
+*/
+ at RunWith(DistributionIntegrationTestRunner.class)
+class LiveOutputIntegrationTest {
+
+    static final String JAVA_PROJECT_NAME = 'javaproject'
+    static final String SHARED_NAME = 'shared'
+    static final String API_NAME = 'api'
+    static final String WEBAPP_NAME = 'webservice'
+    static final String SERVICES_NAME = 'services'
+    static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
+
+    private File javaprojectDir
+
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('java/quickstart')
+
+    @Before
+    void setUp() {
+        javaprojectDir = sample.dir
+    }
+
+    /**
+This executes 'build' on the java multiproject sample. We want to make sure that
+we do get live output from gradle. We're not concerned with what it is, because
+that's likely to change over time. This version executes the command via GradlePlugin.
+
+ at author mhunsicker
+*/
+
+    @Test
+    public void liveOutputObtainedViaGradlePlugin() {
+       System.out.println("project dir: " + javaprojectDir );
+        // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
+
+        File multiProjectDirectory = sample.getDir();
+        Assert.assertTrue(multiProjectDirectory.exists()); //make sure things are setup the way we expect
+
+        GradlePluginLord gradlePluginLord = new GradlePluginLord();
+        gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
+        gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
+
+        gradlePluginLord.startExecutionQueue(); //for tests, we'll need to explicitly start the execution queue (unless we do a refresh via the TestUtility).
+
+        TestExecutionInteraction executionInteraction = new TestExecutionInteraction();
+
+        //execute a command. We don't really care what the command is, just something that generates output
+        TestUtility.executeBlocking( gradlePluginLord, "tasks", "Test Execution", executionInteraction, 80 )
+
+        verifyLiveOutputObtained( executionInteraction );
+    }
+
+    /**
+This executes 'build' on the java multiproject sample. We want to make sure that
+we do get live output from gradle. We're not concerned with what it is, because
+that's likely to change over time. This version executes the command via GradleRunner.
+
+ at author mhunsicker
+*/
+    @Test
+    public void liveOutputObtainedViaGradleRunner() {
+        // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
+
+        File multiProjectDirectory = sample.getDir();
+        Assert.assertTrue(multiProjectDirectory.exists()); //make sure things are setup the way we expect
+
+        GradleRunner gradleRunner = new GradleRunner( multiProjectDirectory, dist.gradleHomeDir, null );
+
+        TestExecutionInteraction executionInteraction = new TestExecutionInteraction();
+
+        //execute a command. We don't really care what the command is, just something that generates output
+        gradleRunner.executeCommand("tasks", org.gradle.api.logging.LogLevel.LIFECYCLE,
+                                            org.gradle.StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS,
+                                            executionInteraction);
+
+        executionInteraction.waitForCompletion(80, TimeUnit.SECONDS)
+
+        verifyLiveOutputObtained( executionInteraction );
+    }
+
+
+
+   /**
+  This verifies that it has live output. It also checks that we received some final output as well
+  as that the execution was successful
+  */
+   private void verifyLiveOutputObtained( TestExecutionInteraction executionInteraction )
+   {
+      executionInteraction.assertCompleted()
+
+      //Make sure we were successful. If we weren't successful, that probably indicates a different problem and the test itself may be invalid.
+      Assert.assertTrue( String.format("Verifying execution was successful failed:%n%s", executionInteraction.finalMessage), executionInteraction.wasSuccessful )
+
+      //verify that we actually finished. If not, then we timed out, which may mean the machine is really slow or that there's a serious problem.
+      Assert.assertTrue( "Verifying execution finished in a timely manner", executionInteraction.executionFinishedReported );
+
+      //make sure we received some output! I just made up 30 because I wanted more than just 1 character and there should actually be dozens of characters.
+      Assert.assertTrue( "Verifying live output was obtained", executionInteraction.liveOutput.length() >= 30 )
+
+      //We should also get a final message. Note: this is usually a little different from the live output, if for not other reason than
+      //timing issues of when the last live output is sent. The final message should have everything, but we might not get the last
+      //live output. As such, we won't verify they're equal.
+      Assert.assertTrue( "Verifying the final output message was received", executionInteraction.finalMessage.length() > 30 )
+   }
+}
+
+//this class just holds onto our liveOutput and also tracks whether or not we've finished.
+public class TestExecutionInteraction implements ExecuteGradleCommandServerProtocol.ExecutionInteraction {
+    private StringBuilder liveOutput = new StringBuilder();
+    public boolean executionFinishedReported = false;
+    public boolean wasSuccessful = false;
+    public String finalMessage;
+    private Throwable failure
+    private final Lock lock = new ReentrantLock()
+    private final Condition condition = lock.newCondition()
+
+    public void reportLiveOutput(String message) {
+        liveOutput.append(message);
+    }
+
+    //when we finish executing, we'll make sure we got some type of live output from gradle.
+
+    public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
+        lock.lock()
+        try {
+            executionFinishedReported = true
+            this.wasSuccessful = wasSuccessful
+            this.finalMessage = message
+            failure = throwable
+            condition.signalAll()
+        } finally {
+            lock.unlock()
+        }
+    }
+
+    def assertCompleted() {
+        lock.lock()
+        try {
+            if (!executionFinishedReported) {
+                throw new AssertionError("Request has not completed.")
+            }
+        } finally {
+            lock.unlock()
+        }
+    }
+
+    public waitForCompletion(int maxWaitValue, TimeUnit maxWaitUnits) {
+        Date expiry = new Date(System.currentTimeMillis() + maxWaitUnits.toMillis(maxWaitValue))
+        lock.lock()
+        try {
+            while (!executionFinishedReported) {
+                if (!condition.awaitUntil(expiry)) {
+                    throw new AssertionError("Timeout waiting for execution to complete.")
+                }
+            }
+            if (failure != null) {
+                throw failure
+            }
+        } finally {
+            lock.unlock()
+        }
+    }
+
+    public void reportExecutionStarted() { }
+
+    public void reportNumberOfTasksToExecute(int size) { }
+
+    public void reportTaskStarted(String message, float percentComplete) { }
+
+    public void reportTaskComplete(String message, float percentComplete) { }
+
+
+}
\ No newline at end of file
diff --git a/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy b/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
new file mode 100644
index 0000000..b732e6f
--- /dev/null
+++ b/subprojects/gradle-ui/src/integTest/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2009 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.integtests
+
+import org.gradle.foundation.ProjectView
+import org.gradle.foundation.TaskView
+import org.gradle.gradleplugin.foundation.GradlePluginLord
+import org.gradle.integtests.fixtures.GradleDistribution
+import org.gradle.integtests.fixtures.GradleDistributionExecuter
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.openapi.external.foundation.ProjectVersion1
+import org.gradle.openapi.wrappers.foundation.GradleInterfaceWrapperVersion1
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+import org.gradle.foundation.TestUtility
+
+/**
+ This tests the multiproject sample with the GradleView mechanism.
+ @author mhunsicker
+ */
+ at RunWith(DistributionIntegrationTestRunner.class)
+class MultiprojectProjectAndTaskListIntegrationTest {
+
+    static final String JAVA_PROJECT_NAME = 'javaproject'
+    static final String SHARED_NAME = 'shared'
+    static final String API_NAME = 'api'
+    static final String WEBAPP_NAME = 'webservice'
+    static final String SERVICES_NAME = 'services'
+    static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
+
+    private File javaprojectDir
+
+    @Rule public final GradleDistribution dist = new GradleDistribution()
+    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
+    @Rule public final Sample sample = new Sample('java/multiproject')
+
+    @Before
+    void setUp() {
+        javaprojectDir = sample.getDir()
+    }
+
+    /*
+       This tests against the multiproject sample. It expects to find not just
+       the root level projects, but also the nested sub projects
+       (services:webservice). This isn't really interested in the actual tasks
+       themselves (I fear those may change too often to worry with keeping the
+       test up to date).
+
+       @author mhunsicker
+    */
+
+    @Test
+    public void multiProjectjavaProjectSample() {
+        // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
+
+        File multiProjectDirectory = sample.getDir();
+        Assert.assertTrue(multiProjectDirectory.exists());
+
+        GradlePluginLord gradlePluginLord = new GradlePluginLord();
+        gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
+        gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
+
+        //refresh the projects and wait. This will throw an exception if it fails.
+        TestUtility.refreshProjectsBlocking(gradlePluginLord, 80, TimeUnit.SECONDS);
+
+        //get the root project
+        List<ProjectView> projects = gradlePluginLord.getProjects();
+        Assert.assertNotNull(projects);
+
+        //make sure there weren't other root projects found.
+        Assert.assertEquals(1, projects.size());
+
+        ProjectView rootProject = projects.get(0);
+        Assert.assertNotNull(rootProject);
+        Assert.assertEquals("multiproject", rootProject.getName());
+
+        //now check for sub projects, api, shared, and services
+        ProjectView apiProject = rootProject.getSubProject("api");
+        Assert.assertNotNull(apiProject);
+        Assert.assertTrue(apiProject.getSubProjects().isEmpty());  //this has no sub projects
+
+        ProjectView sharedProject = rootProject.getSubProject("shared");
+        Assert.assertNotNull(sharedProject);
+        Assert.assertTrue(sharedProject.getSubProjects().isEmpty());  //this has no sub projects
+
+        ProjectView servicesProject = rootProject.getSubProject("services");
+        Assert.assertNotNull(servicesProject);
+
+        //services has a sub project
+        ProjectView webservicesProject = servicesProject.getSubProject("webservice");
+        Assert.assertNotNull(webservicesProject);
+        Assert.assertTrue(webservicesProject.getSubProjects().isEmpty());  //this has no sub projects
+
+        //make sure we didn't inadvertantly find other sub projects.
+        Assert.assertEquals(3, rootProject.getSubProjects().size());
+    }
+
+   /**
+    This tests that the wrappers for projects and tasks are working
+    */
+   @Test
+   public void testOpenAPIWrapperProjectAndTaskList()
+   {
+     // Build and test projects
+        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
+
+        File multiProjectDirectory = sample.getDir();
+        Assert.assertTrue(multiProjectDirectory.exists());
+
+        GradlePluginLord gradlePluginLord = new GradlePluginLord();
+        gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
+        gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
+
+        GradleInterfaceWrapperVersion1 wrapper = new GradleInterfaceWrapperVersion1( gradlePluginLord );
+
+        //the rest of this uses the open API mechanism to access the projects and tasks
+
+        //refresh the projects and wait. This will throw an exception if it fails.
+        TestUtility.refreshProjectsBlocking(gradlePluginLord, 80, TimeUnit.SECONDS);
+
+        //get the root project
+        List<ProjectVersion1> projects = wrapper.getRootProjects();
+        Assert.assertNotNull(projects);
+
+        //make sure there weren't other root projects found.
+        Assert.assertEquals(1, projects.size());
+
+        ProjectVersion1 rootProject = projects.get(0);
+        Assert.assertNotNull(rootProject);
+        Assert.assertEquals("multiproject", rootProject.getName());
+
+        //now check for sub projects, api, shared, and services
+        ProjectVersion1 apiProject = rootProject.getSubProject("api");
+        Assert.assertNotNull(apiProject);
+        Assert.assertTrue(apiProject.getSubProjects().isEmpty());  //this has no sub projects
+
+        ProjectVersion1 sharedProject = rootProject.getSubProject("shared");
+        Assert.assertNotNull(sharedProject);
+        Assert.assertTrue(sharedProject.getSubProjects().isEmpty());  //this has no sub projects
+
+        ProjectVersion1 servicesProject = rootProject.getSubProject("services");
+        Assert.assertNotNull(servicesProject);
+
+        //services has a sub project
+        ProjectVersion1 webservicesProject = servicesProject.getSubProject("webservice");
+        Assert.assertNotNull(webservicesProject);
+        Assert.assertTrue(webservicesProject.getSubProjects().isEmpty());  //this has no sub projects
+
+        //make sure we didn't inadvertantly find other sub projects.
+        Assert.assertEquals(3, rootProject.getSubProjects().size());
+
+        //I don't want to keep the actual tasks in synch, but let's make sure there's something there.
+        def tasks = apiProject.getTasks()
+        Assert.assertNotNull( tasks );
+        Assert.assertFalse( tasks.isEmpty() );
+   }
+
+   /**
+   * This tests ProjectView.getSubProjectFromFullPath. Specifically, the first character
+    * is optionally a colon. So this tests it both ways.
+   */
+   @Test
+   public void testSubProjectFromFullPath()
+   {
+     executer.inDirectory(javaprojectDir).withTasks('assemble').run();
+
+      File multiProjectDirectory = sample.getDir();
+      Assert.assertTrue(multiProjectDirectory.exists());
+
+      GradlePluginLord gradlePluginLord = new GradlePluginLord();
+      gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
+      gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
+
+      //refresh the projects and wait. This will throw an exception if it fails.
+      TestUtility.refreshProjectsBlocking(gradlePluginLord, 80, TimeUnit.SECONDS);
+
+      //get the root project
+      List<ProjectView> projects = gradlePluginLord.getProjects();
+      Assert.assertNotNull(projects);
+      Assert.assertFalse( projects.isEmpty() );
+
+      ProjectView rootProject = projects.get(0)
+
+      //test it using no prefixed colon
+      ProjectView foundProject1 = rootProject.getSubProjectFromFullPath("services:webservice")
+      Assert.assertNotNull( foundProject1 )
+
+      //test it using a prefixed colon
+      ProjectView foundProject2 = rootProject.getSubProjectFromFullPath(":services:webservice")
+      Assert.assertNotNull( foundProject2 )
+
+      //should both the same project
+      Assert.assertEquals( foundProject1, foundProject2 )
+   }
+
+   /**
+   * This tests TaskView.getTaskFromFullPath. Specifically, the first character
+    * is optionally a colon. So this tests it both ways.
+   */
+   @Test
+   public void testGetTaskFromFullPath()
+   {
+     executer.inDirectory(javaprojectDir).withTasks('assemble').run();
+
+      File multiProjectDirectory = sample.getDir();
+      Assert.assertTrue(multiProjectDirectory.exists());
+
+      GradlePluginLord gradlePluginLord = new GradlePluginLord();
+      gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
+      gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
+
+      //refresh the projects and wait. This will throw an exception if it fails.
+      TestUtility.refreshProjectsBlocking(gradlePluginLord, 100, TimeUnit.SECONDS);
+
+      //get the root project
+      List<ProjectView> projects = gradlePluginLord.getProjects();
+      Assert.assertNotNull(projects);
+      Assert.assertFalse( projects.isEmpty() );
+
+      ProjectView rootProject = projects.get(0)
+
+      //test it using no prefixed colon
+      TaskView foundTask1 = rootProject.getTaskFromFullPath("api:build")
+      Assert.assertNotNull( foundTask1 )
+
+      //test it using a prefixed colon
+      TaskView foundTask2 = rootProject.getTaskFromFullPath(":api:build")
+      Assert.assertNotNull( foundTask2 )
+
+      //should both the same project
+      Assert.assertEquals( foundTask1, foundTask2 )      
+   }
+}
\ No newline at end of file
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/CommandLineAssistant.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/CommandLineAssistant.java
index 360b337..a16b7ad 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/CommandLineAssistant.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/CommandLineAssistant.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 the original author or authors.
+ * Copyright 2010 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,7 +15,8 @@
  */
 package org.gradle.foundation;
 
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
+import org.gradle.initialization.DefaultCommandLineConverter;
+import org.gradle.logging.internal.LoggingCommandLineConverter;
 
 import java.util.List;
 import java.util.ArrayList;
@@ -27,14 +28,19 @@ import java.util.Iterator;
  * @author mhunsicker
  */
 public class CommandLineAssistant {
-    private DefaultCommandLine2StartParameterConverter commandLine2StartParameterConverter;
+    private final DefaultCommandLineConverter commandLineConverter;
+    private final LoggingCommandLineConverter loggingCommandLineConverter = new LoggingCommandLineConverter();
 
     public CommandLineAssistant() {
-        commandLine2StartParameterConverter = new DefaultCommandLine2StartParameterConverter();
+        commandLineConverter = new DefaultCommandLineConverter();
     }
 
-    public DefaultCommandLine2StartParameterConverter getCommandLine2StartParameterConverter() {
-        return commandLine2StartParameterConverter;
+    public DefaultCommandLineConverter getCommandLineConverter() {
+        return commandLineConverter;
+    }
+
+    public LoggingCommandLineConverter getLoggingCommandLineConverter() {
+        return loggingCommandLineConverter;
     }
 
     /**
@@ -47,29 +53,31 @@ public class CommandLineAssistant {
     public static String[] breakUpCommandLine(String fullCommandLine) {
         List<String> commandLineArguments = new ArrayList<String>();
 
-        boolean isInsideQuotes = false;
-        StringBuffer currentOption = new StringBuffer();
+        Character currentQuote = null;
+        StringBuilder currentOption = new StringBuilder();
+        boolean hasOption = false;
 
         for (int index = 0; index < fullCommandLine.length(); index++) {
             char c = fullCommandLine.charAt(index);
-            if (Character.isSpaceChar(c) && !isInsideQuotes) {
-                currentOption.trimToSize();
-                if (currentOption.length() > 0) {
+            if (currentQuote == null && Character.isWhitespace(c)) {
+                if (hasOption) {
                     commandLineArguments.add(currentOption.toString());
+                    hasOption = false;
+                    currentOption.setLength(0);
                 }
-
-                currentOption = new StringBuffer();
-            } else {
-                if (c == '"') {
-                    isInsideQuotes = !isInsideQuotes;
-                }
-
+            } else if (currentQuote == null && (c == '"' || c == '\'')) {
+                currentQuote = c;
+                hasOption = true;
+            } else if (currentQuote != null && c == currentQuote) {
+                currentQuote = null;
+            }
+            else {
                 currentOption.append(c);
+                hasOption = true;
             }
         }
 
-        currentOption.trimToSize();
-        if (currentOption.length() > 0) {
+        if (hasOption) {
             commandLineArguments.add(currentOption.toString());
         }
 
@@ -80,7 +88,7 @@ public class CommandLineAssistant {
         return hasCommandLineOptionsDefined(commandLineArguments, new CommandLineSearch() {
             public boolean contains(String commandLine) {
 
-                return commandLine2StartParameterConverter.getLogLevel(commandLine) != null;
+                return loggingCommandLineConverter.getLogLevel(commandLine) != null;
             }
         });
     }
@@ -88,7 +96,7 @@ public class CommandLineAssistant {
     public boolean hasShowStacktraceDefined(String[] commandLineArguments) {
         return hasCommandLineOptionsDefined(commandLineArguments, new CommandLineSearch() {
             public boolean contains(String commandLine) {
-                return commandLine2StartParameterConverter.getShowStacktrace(commandLine) != null;
+                return commandLineConverter.getShowStacktrace(commandLine) != null;
             }
         });
     }
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectConverter.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectConverter.java
index 3a0166d..e3548ca 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectConverter.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectConverter.java
@@ -20,11 +20,7 @@ import org.gradle.api.Task;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 /**
  * This converts Gradle's projects into ProjectView objects. These can be safely reused unlike Gradle's projects.
@@ -62,12 +58,12 @@ public class ProjectConverter {
      * @param rootLevelProject a root level project.
      */
     public void addRootLevelProject(Project rootLevelProject) {
-        ProjectView rootLevelProjectView = new ProjectView(rootLevelProject.getName(), rootLevelProject.getBuildFile());
+        ProjectView rootLevelProjectView = new ProjectView(null, rootLevelProject.getName(), rootLevelProject.getBuildFile(), rootLevelProject.getDescription());
         projectMap.put(rootLevelProject, rootLevelProjectView);
 
         rootLevelResultingProjects.add(rootLevelProjectView);
 
-        addSubProjects(rootLevelProject, rootLevelProjectView, 1);
+        addSubProjects(rootLevelProject, rootLevelProjectView);
 
         addTasks(rootLevelProject, rootLevelProjectView);
 
@@ -75,31 +71,22 @@ public class ProjectConverter {
     }
 
     /**
-     * Adds all sub projects of the specifed GradleProject.
+     * Adds all sub projects of the specified GradleProject.
      *
      * @param parentProject the source parent project. Where we get the sub projects from.
      * @param parentProjectView the destination of the sub projects from parentProject.
      */
-    private void addSubProjects(Project parentProject, ProjectView parentProjectView, int currentDepth) {
-        Set<Project> subProjects = parentProject.getSubprojects();
-        Iterator<Project> iterator = subProjects.iterator();
-        while (iterator.hasNext()) {
-            Project subProject = iterator.next();
-            int depth = subProject.getDepth();
-            if (depth
-                    == currentDepth)   //at the root, we seem to be getting all projects regardless of their depth (that is we'll get root:subproject1:subproject2 as the root's subproject). We'll ignore these and then add them to our hierarchy when we get to the correct depth.
-            {
-                ProjectView projectView = new ProjectView(subProject.getName(), subProject.getBuildFile());
-                projectMap.put(subProject, projectView);
+    private void addSubProjects(Project parentProject, ProjectView parentProjectView) {
+        Collection<Project> subProjects = parentProject.getChildProjects().values();
+        for (Project subProject : subProjects) {
+            ProjectView projectView = new ProjectView(parentProjectView, subProject.getName(), subProject.getBuildFile(), subProject.getDescription());
+            projectMap.put(subProject, projectView);
 
-                parentProjectView.addSubProject(projectView);
+            addTasks(subProject, projectView);
 
-                addTasks(subProject, projectView);
+            projectView.sortSubProjectsAndTasks();
 
-                projectView.sortSubProjectsAndTasks();
-
-                addSubProjects(subProject, projectView, currentDepth + 1);
-            }
+            addSubProjects(subProject, projectView);
         }
     }
 
@@ -111,8 +98,7 @@ public class ProjectConverter {
      */
     private void addTasks(Project project, ProjectView projectView) {
         List<String> defaultTasks = project.getDefaultTasks();
-        Set<Task> tasks = project.getTasks().getAll();
-        for (Task task : tasks) {
+        for (Task task : project.getTasks()) {
             String taskName = task.getName();
 
             boolean isDefault = defaultTasks.contains(taskName);
@@ -126,10 +112,7 @@ public class ProjectConverter {
      * them to ProjectViews. Obviously, this must be done after converting all Projects to ProjectViews.
      */
     private void buildDependencies() {
-        Iterator<Project> projectIterator = projectMap.keySet().iterator();
-        while (projectIterator.hasNext()) {
-            Project project = projectIterator.next();
-
+        for (Project project : projectMap.keySet()) {
             ProjectView projectView = projectMap.get(project);
 
             List<ProjectView> projectViewList = getProjectViews(project.getDependsOnProjects());
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectView.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectView.java
index 53f0e52..b63a9c8 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectView.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ProjectView.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.foundation;
 
+import org.gradle.util.GUtil;
+
 import java.io.File;
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -31,22 +33,28 @@ import java.util.List;
  * @author mhunsicker
  */
 public class ProjectView implements Comparable<ProjectView>, Serializable {
-    private String name;
-    private ProjectView parentProject;
-            //will be null for any project until it is added as a sub project to another project. It is null for the root project always.
-    private List<ProjectView> subProjects = new ArrayList<ProjectView>();
-    private List<TaskView> tasks = new ArrayList<TaskView>();
-    private List<ProjectView> dependsOnProjects = new ArrayList<ProjectView>();
+    private final String name;
+    private final ProjectView parentProject;
+            // It is null for the root project.
+    private final List<ProjectView> subProjects = new ArrayList<ProjectView>();
+    private final List<TaskView> tasks = new ArrayList<TaskView>();
+    private final List<ProjectView> dependsOnProjects = new ArrayList<ProjectView>();
 
-    private File buildFile;
+    private final File buildFile;
+    private final String description;
 
     /**
      * Instantiates an immutable view of a project. This is only meant to be called internally whenever generating a
-     * hierachy of projects and tasks.
+     * hierarchy of projects and tasks.
      */
-    /*package*/ ProjectView(String name, File buildFile) {
+    /*package*/ ProjectView(ProjectView parentProject, String name, File buildFile, String description) {
+        this.parentProject = parentProject;
         this.name = name;
         this.buildFile = buildFile;
+        this.description = GUtil.elvis(description, "");
+        if (parentProject != null) {
+            parentProject.addSubProject(this);
+        }
     }
 
     public String getName() {
@@ -57,6 +65,10 @@ public class ProjectView implements Comparable<ProjectView>, Serializable {
         return buildFile;
     }
 
+    public String getDescription() {
+        return description;
+    }
+
     public String toString() {
         return name;
     }
@@ -65,10 +77,6 @@ public class ProjectView implements Comparable<ProjectView>, Serializable {
         return parentProject;
     }
 
-    protected void setParentProject(ProjectView parentProject) {
-        this.parentProject = parentProject;
-    }
-
     public int compareTo(ProjectView otherProject) {
         return name.compareTo(otherProject.name);
     }
@@ -87,7 +95,6 @@ public class ProjectView implements Comparable<ProjectView>, Serializable {
      * generating a hierachy of projects and tasks.
      */
     /*package*/ void addSubProject(ProjectView subProject) {
-        subProject.setParentProject(this);
         subProjects.add(subProject);
     }
 
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/TaskView.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/TaskView.java
index 94ce92d..48d2fb9 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/TaskView.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/TaskView.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.foundation;
 
+import org.gradle.util.GUtil;
+
 import java.io.Serializable;
 
 /**
@@ -29,23 +31,17 @@ public class TaskView implements Comparable<TaskView>, Serializable {
     private String name;
     private String description;
     private boolean isDefault;
-            //whether or not this is one of potentially manny default tasks for its project.
+            //whether or not this is one of potentially many default tasks for its project.
 
     /**
-     * Instantiates an immutable view of a task. This is only meant to be called internally whenever generating a hierachy
+     * Instantiates an immutable view of a task. This is only meant to be called internally whenever generating a hierarchy
      * of projects and tasks.
      */
     /*package*/ TaskView(ProjectView project, String name, String description, boolean isDefault) {
         this.project = project;
         this.name = name;
         this.isDefault = isDefault;
-
-        if (description == null || description.trim().equals("")) {
-            this.description
-                    = "";  //just make a blank or null description empty so we don't have to check for null everywhere.
-        } else {
-            this.description = description;
-        }
+        this.description = GUtil.elvis(description, "");
     }
 
     public ProjectView getProject() {
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/common/ReorderableList.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/common/ReorderableList.java
index 603bd41..9846b79 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/common/ReorderableList.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/common/ReorderableList.java
@@ -474,7 +474,7 @@ public class ReorderableList<E> implements List<E> {
      *  Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole
      * list.  For example, the following idiom removes a range of elements from a list:
      * <pre>
-     * 	    list.subList(from, to).clear();
+     *     list.subList(from, to).clear();
      * </pre>
      * Similar idioms may be constructed for <tt>indexOf</tt> and <tt>lastIndexOf</tt>, and all of the algorithms in the
      * <tt>Collections</tt> class can be applied to a subList.<p> <p/> The semantics of the list returned by this method
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/ProcessLauncherServer.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/ProcessLauncherServer.java
index 9ccd11a..586134b 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/ProcessLauncherServer.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/ProcessLauncherServer.java
@@ -36,7 +36,7 @@ import java.io.ByteArrayOutputStream;
 public class ProcessLauncherServer extends Server<ProcessLauncherServer.Protocol, ProcessLauncherServer.ServerObserver> {
     private volatile ExecHandle externalProcess;
 
-    private final Logger logger = Logging.getLogger(ProcessLauncherServer.class);
+    private static final Logger LOGGER = Logging.getLogger(ProcessLauncherServer.class);
 
     /**
      * Implement this to define the behavior of the communication on the server side.
@@ -108,13 +108,14 @@ public class ProcessLauncherServer extends Server<ProcessLauncherServer.Protocol
                     execHandle.start();
                 }
                 catch (Throwable e) {
-                    logger.error("Starting external process", e);
+                    LOGGER.error("Starting external process", e);
                     notifyClientExited( -1, e.getMessage() );
                     setExternalProcess(null);
                     return;
                 }
 
                 ExecResult result = execHandle.waitForFinish();
+                LOGGER.debug("External process completed with exit code {}", result.getExitValue());
 
                 setExternalProcess(null);   //clear our external process member variable (we're using our local variable below). This is so we know the process has already stopped.
 
@@ -136,7 +137,7 @@ public class ProcessLauncherServer extends Server<ProcessLauncherServer.Protocol
     }
 
     /**
-     * Call this to violently kill the external process. This is NOT a good way to stop it. It is preferrable to ask the
+     * Call this to violently kill the external process. This is NOT a good way to stop it. It is preferable to ask the
      * thread to stop. However, gradle has no way to do that, so we'll be killing it.
      */
     public synchronized void killProcess() {
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/Server.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/Server.java
index ce55f8a..1b335ed 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/Server.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/basic/Server.java
@@ -65,7 +65,7 @@ public class Server<P extends Server.Protocol, O extends Server.ServerObserver>
         public void connectionAccepted();
 
         /**
-         * @return true if we should keep the connection alive. False if we should stop communicaiton.
+         * @return true if we should keep the connection alive. False if we should stop communication.
          */
         public boolean continueConnection();
 
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java
index b7e1c0f..6a0a6a6 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/AbstractGradleServerProtocol.java
@@ -16,7 +16,7 @@
 package org.gradle.foundation.ipc.gradle;
 
 import org.apache.commons.io.IOUtils;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
+import org.gradle.initialization.DefaultCommandLineConverter;
 import org.gradle.StartParameter;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
@@ -26,6 +26,7 @@ import org.gradle.foundation.ipc.basic.MessageObject;
 import org.gradle.foundation.ipc.basic.ProcessLauncherServer;
 import org.gradle.foundation.ipc.basic.ExecutionInfo;
 import org.gradle.foundation.ipc.basic.ClientProcess;
+import org.gradle.util.OperatingSystem;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -272,15 +273,14 @@ public abstract class AbstractGradleServerProtocol implements ProcessLauncherSer
       File initStriptPath = getInitScriptFile();
       if( initStriptPath != null )
       {
-          executionCommandLine.add( "-" + DefaultCommandLine2StartParameterConverter.INIT_SCRIPT );
+          executionCommandLine.add( "-" + DefaultCommandLineConverter.INIT_SCRIPT );
           executionCommandLine.add( initStriptPath.getAbsolutePath() );
           executionInfo.initStriptPath = initStriptPath;
       }
 
       //add the log level if its not present
         if (!commandLineAssistant.hasLogLevelDefined(individualCommandLineArguments)) {
-            String logLevelText = commandLineAssistant.getCommandLine2StartParameterConverter().getLogLevelCommandLine(
-                    logLevel);
+            String logLevelText = commandLineAssistant.getLoggingCommandLineConverter().getLogLevelCommandLine(logLevel);
             if (logLevelText != null && !"".equals(logLevelText)) {
             executionCommandLine.add( '-' + logLevelText );
       }
@@ -288,8 +288,7 @@ public abstract class AbstractGradleServerProtocol implements ProcessLauncherSer
 
       //add the stack trace level if its not present
         if (!commandLineAssistant.hasShowStacktraceDefined(individualCommandLineArguments)) {
-            String stackTraceLevelText = commandLineAssistant.getCommandLine2StartParameterConverter()
-                    .getShowStacktraceCommandLine(stackTraceLevel);
+            String stackTraceLevelText = commandLineAssistant.getCommandLineConverter().getShowStacktraceCommandLine(stackTraceLevel);
             if (stackTraceLevelText != null) {
             executionCommandLine.add( '-' + stackTraceLevelText );
       }
@@ -318,11 +317,7 @@ public abstract class AbstractGradleServerProtocol implements ProcessLauncherSer
    */
    private String getDefaultGradleExecutableName()
    {
-      String osName = System.getProperty("os.name");
-        if (osName.indexOf("indows") >= 0) {
-            return "gradle.bat";
-        } //windoes uses a batch file
-      return "gradle";        //all others use a shell script
+       return OperatingSystem.current().getScriptName("gradle");
    }
 
    /**
@@ -444,8 +439,12 @@ public abstract class AbstractGradleServerProtocol implements ProcessLauncherSer
       try
       {
          fileOutputStream = new FileOutputStream( file );
-         IOUtils.write( bytes, fileOutputStream );
-         return true;
+          try {
+              IOUtils.write( bytes, fileOutputStream );
+          } finally {
+              fileOutputStream.close();
+          }
+          return true;
       }
       catch( IOException e )
       {
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/IPCUtilities.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/IPCUtilities.java
index db05e75..6a9aada 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/IPCUtilities.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/IPCUtilities.java
@@ -41,7 +41,7 @@ public class IPCUtilities {
 
         ExecuteGradleCommandClientProtocol protocol = new ExecuteGradleCommandClientProtocol(gradle);
         GradleClient client = new GradleClient();
-        client.start(protocol, port.intValue());
+        client.start(protocol, port);
     }
 
     /**
@@ -61,8 +61,7 @@ public class IPCUtilities {
         }
 
         try {
-            int port = Integer.parseInt(portText);
-            return new Integer(port);
+            return Integer.parseInt(portText);
         } catch (NumberFormatException e) {
             LOGGER.error("Invalid " + ProtocolConstants.PORT_NUMBER_SYSTEM_PROPERTY + " system property", e);
             return null;
@@ -71,7 +70,7 @@ public class IPCUtilities {
 
     /**
      * This starts a gradle client that sends a task list back to the server. It expects the port number to set as a
-     * system property. You probably should be executing the "-t" command. Note: this is using gradle to find the port.
+     * system property. You probably should be executing the "tasks" command. Note: this is using gradle to find the port.
      * See getPort().
      *
      * @param gradle the gradle launcher object.
@@ -84,6 +83,6 @@ public class IPCUtilities {
 
         TaskListClientProtocol protocol = new TaskListClientProtocol(gradle);
         GradleClient client = new GradleClient();
-        client.start(protocol, port.intValue());
+        client.start(protocol, port);
     }
 }
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
index e27a6a7..4cf4031 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListClientProtocol.java
@@ -15,9 +15,8 @@
  */
 package org.gradle.foundation.ipc.gradle;
 
-import org.gradle.BuildListener;
+import org.gradle.BuildAdapter;
 import org.gradle.BuildResult;
-import org.gradle.api.initialization.Settings;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -66,7 +65,7 @@ public class TaskListClientProtocol implements ClientProcess.Protocol {
      * Listener used to delegate gradle messages to our listeners.
      *
      */
-    private class RefreshTaskListBuildListener implements BuildListener, StandardOutputListener
+    private class RefreshTaskListBuildListener extends BuildAdapter implements StandardOutputListener
     {
         private ClientProcess client;
         private StringBuffer allOutputText = new StringBuffer(); //this is potentially threaded, so use StringBuffer instead of StringBuilder
@@ -81,41 +80,11 @@ public class TaskListClientProtocol implements ClientProcess.Protocol {
         }
 
        /**
-        * <p>Called when the build is started.</p>
-        *
-        * @param gradle The build which is being started. Never null.
-        */
-       public void buildStarted( Gradle gradle ) { }
-
-       /**
-        * <p>Called when the build settings have been loaded and evaluated. The settings object is fully configured and is
-        * ready to use to load the build projects.</p>
-        *
-        * @param settings The settings. Never null.
-        */
-       public void settingsEvaluated( Settings settings ) { }
-
-       /**
-        * <p>Called when the projects for the build have been created from the settings. None of the projects have been
-        * evaluated.</p>
-        *
-        * @param gradle The build which has been loaded. Never null.
-        */
-       public void projectsLoaded( Gradle gradle ) { }
-
-       /**
-        * <p>Called when all projects for the build have been evaluated. The project objects are fully configured and are
-        * ready to use to populate the task graph.</p>
-        *
-        * @param gradle The build which has been evaluated. Never null.
-        */
-       public void projectsEvaluated( Gradle gradle ) { }
-
-       /**
         * <p>Called when the build is completed. All selected tasks have been executed.</p>
         *
-        * @param result The result of the build. Never null.
+        * @param buildResult The result of the build. Never null.
         */
+       @Override
        public void buildFinished( BuildResult buildResult )
        {
          boolean wasSuccessful = buildResult.getFailure() == null;
@@ -128,7 +97,7 @@ public class TaskListClientProtocol implements ClientProcess.Protocol {
              String details = GradlePluginLord.getGradleExceptionMessage(buildResult.getFailure(), gradle.getStartParameter().getShowStacktrace());
              output += details;
 
-             client.sendMessage(ProtocolConstants.TASK_LIST_COMPLETED_WITH_ERRORS_TYPE, output, new Boolean(wasSuccessful));
+             client.sendMessage(ProtocolConstants.TASK_LIST_COMPLETED_WITH_ERRORS_TYPE, output, wasSuccessful);
          } else {
              ProjectConverter buildExecuter = new ProjectConverter();
              List<ProjectView> projects = new ArrayList<ProjectView>();
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java
index d62ddf2..7fa1313 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/foundation/ipc/gradle/TaskListServerProtocol.java
@@ -53,7 +53,6 @@ public class TaskListServerProtocol extends AbstractGradleServerProtocol {
         * @param  wasSuccessful true if gradle was successful (returned 0)
         * @param  message       the output of gradle if it ran. If it didn't, an error message.
         * @param  throwable     an exception if one occurred
-        * @param  projects      a hierachical list of projects. This is the final result.
         */
         void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable);
 
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java
index 76231c9..d800afc 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/GradlePluginLord.java
@@ -16,20 +16,20 @@
 package org.gradle.gradleplugin.foundation;
 
 import org.codehaus.groovy.runtime.StackTraceUtils;
+import org.gradle.StartParameter;
 import org.gradle.api.LocationAwareException;
 import org.gradle.api.internal.DefaultClassPathProvider;
-import org.gradle.gradleplugin.foundation.favorites.FavoriteTask;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
-import org.gradle.StartParameter;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.configuration.ImplicitTasksConfigurer;
 import org.gradle.foundation.CommandLineAssistant;
 import org.gradle.foundation.ProjectView;
 import org.gradle.foundation.TaskView;
 import org.gradle.foundation.common.ObserverLord;
 import org.gradle.foundation.ipc.basic.ProcessLauncherServer;
 import org.gradle.foundation.queue.ExecutionQueue;
+import org.gradle.gradleplugin.foundation.favorites.FavoriteTask;
 import org.gradle.gradleplugin.foundation.favorites.FavoritesEditor;
 import org.gradle.gradleplugin.foundation.request.ExecutionRequest;
 import org.gradle.gradleplugin.foundation.request.RefreshTaskListRequest;
@@ -37,11 +37,7 @@ import org.gradle.gradleplugin.foundation.request.Request;
 import org.gradle.util.GUtil;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Formatter;
-import java.util.List;
-import java.util.Iterator;
+import java.util.*;
 import java.util.concurrent.LinkedBlockingQueue;
 
 /**
@@ -318,7 +314,7 @@ public class GradlePluginLord {
       {
           public void notify( RequestObserver observer )
           {
-             try { //wrap this in a try/catch block so execeptions in the observer doesn't stop everything
+             try { //wrap this in a try/catch block so exceptions in the observer doesn't stop everything
                 observer.aboutToExecuteRequest( request );
              }
              catch( Exception e )
@@ -335,7 +331,7 @@ public class GradlePluginLord {
       {
           public void notify( RequestObserver observer )
           {
-             try { //wrap this in a try/catch block so execeptions in the observer doesn't stop everything
+             try { //wrap this in a try/catch block so exceptions in the observer doesn't stop everything
                 observer.requestExecutionComplete( request, result, output );
              }
              catch( Exception e )
@@ -513,7 +509,6 @@ public class GradlePluginLord {
         fullCommandLine = alterCommandLine(fullCommandLine);
 
         final ExecutionRequest request = new ExecutionRequest( getNextRequestID(), fullCommandLine, displayName, forceOutputToBeShown, executionQueue );
-        executionQueue.addRequestToQueue(request);
         requestObserverLord.notifyObservers( new ObserverLord.ObserverNotification<RequestObserver>()
         {
            public void notify( RequestObserver observer )
@@ -521,6 +516,7 @@ public class GradlePluginLord {
               observer.executionRequestAdded( request );
            }
         } );
+        executionQueue.addRequestToQueue(request);
         return request;
     }
 
@@ -556,7 +552,7 @@ public class GradlePluginLord {
        //we'll request a task list since there is no way to do a no op. We're not really interested
         //in what's being executed, just the ability to get the task list (which must be populated as
         //part of executing anything).
-        String fullCommandLine = '-' + DefaultCommandLine2StartParameterConverter.TASKS;
+        String fullCommandLine = ImplicitTasksConfigurer.TASKS_TASK;
 
         if( additionalCommandLineArguments != null )
         {
@@ -568,6 +564,7 @@ public class GradlePluginLord {
 
         final RefreshTaskListRequest request = new RefreshTaskListRequest( getNextRequestID(), fullCommandLine, executionQueue, this);
         executionQueue.addRequestToQueue(request);
+        // TODO - fix this race condition - request may already have completed
         requestObserverLord.notifyObservers( new ObserverLord.ObserverNotification<RequestObserver>()
         {
            public void notify( RequestObserver observer )
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/request/RefreshTaskListRequest.java b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/request/RefreshTaskListRequest.java
index 1275881..6afc01e 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/request/RefreshTaskListRequest.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/foundation/request/RefreshTaskListRequest.java
@@ -28,7 +28,7 @@ import java.io.File;
 import java.util.List;
 
 /**
- * This represents a reques to gradle that is executed in a separate process using the ProcessLauncherServer. This is a
+ * This represents a request to gradle that is executed in a separate process using the ProcessLauncherServer. This is a
  * special request where the results are to build up a project/task tree.
  *
  * @author mhunsicker
@@ -87,7 +87,6 @@ public class RefreshTaskListRequest extends AbstractRequest {
          * @param wasSuccessful true if gradle was successful (returned 0)
          * @param message       the output of gradle if it ran. If it didn't, an error message.
          * @param throwable     an exception if one occurred
-         * @param projects      a hierachical list of projects. This is the final result.
          */
         public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
             executionInteraction.reportExecutionFinished(wasSuccessful, message, throwable);
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java
index 615b6ff..eb32e60 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/common/PreferencesAssistant.java
@@ -86,12 +86,10 @@ public class PreferencesAssistant {
             return null;
         }
 
-        int x, y, width, height;
-
-        x = childNode.getValueOfChildAsInt(WINDOW_X, window.getLocation().x);
-        y = childNode.getValueOfChildAsInt(WINDOW_Y, window.getLocation().y);
-        width = childNode.getValueOfChildAsInt(WINDOW_WIDTH, window.getSize().width);
-        height = childNode.getValueOfChildAsInt(WINDOW_HEIGHT, window.getSize().height);
+        int x = childNode.getValueOfChildAsInt(WINDOW_X, window.getLocation().x);
+        int y = childNode.getValueOfChildAsInt(WINDOW_Y, window.getLocation().y);
+        int width = childNode.getValueOfChildAsInt(WINDOW_WIDTH, window.getSize().width);
+        int height = childNode.getValueOfChildAsInt(WINDOW_HEIGHT, window.getSize().height);
 
         window.setLocation(x, y);
         window.setSize(width, height);
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/TaskTreeComponent.java b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/TaskTreeComponent.java
index 147ecce..bae1a91 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/TaskTreeComponent.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/TaskTreeComponent.java
@@ -493,6 +493,11 @@ public class TaskTreeComponent {
             interaction.projectInvoked(project);
         }
 
+        @Override
+        public String getDescription() {
+            return project.getDescription();
+        }
+
         public ProjectView getProject() {
             return project;
         }
diff --git a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java
index b4084bd..8c1f2ea 100644
--- a/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java
+++ b/subprojects/gradle-ui/src/main/java/org/gradle/gradleplugin/userinterface/swing/generic/tabs/SetupTab.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.gradleplugin.userinterface.swing.generic.tabs;
 
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
+import org.gradle.initialization.DefaultCommandLineConverter;
 import org.gradle.StartParameter;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
@@ -24,6 +24,7 @@ import org.gradle.gradleplugin.foundation.GradlePluginLord;
 import org.gradle.gradleplugin.foundation.settings.SettingsNode;
 import org.gradle.gradleplugin.userinterface.swing.generic.OutputUILord;
 import org.gradle.gradleplugin.userinterface.swing.generic.Utility;
+import org.gradle.logging.internal.LoggingCommandLineConverter;
 
 import javax.swing.AbstractAction;
 import javax.swing.AbstractButton;
@@ -274,7 +275,7 @@ public class SetupTab implements GradleTab, GradlePluginLord.SettingsObserver {
     * This creates an array of wrapper objects suitable for passing to the constructor of the log level combo box.
     */
     private Vector<LogLevelWrapper> getLogLevelWrappers() {
-        Collection<LogLevel> collection = new DefaultCommandLine2StartParameterConverter().getLogLevels();
+        Collection<LogLevel> collection = new LoggingCommandLineConverter().getLogLevels();
 
         Vector<LogLevelWrapper> wrappers = new Vector<LogLevelWrapper>();
 
@@ -309,7 +310,7 @@ public class SetupTab implements GradleTab, GradlePluginLord.SettingsObserver {
             this.toString = Character.toUpperCase(temp.charAt(0)) + temp.substring(1);
 
             //add the command line character to the end (so if an error message says use a log level, you can easily translate)
-            String commandLineCharacter = new DefaultCommandLine2StartParameterConverter().getLogLevelCommandLine( logLevel );
+            String commandLineCharacter = new LoggingCommandLineConverter().getLogLevelCommandLine( logLevel );
             if( commandLineCharacter != null && !commandLineCharacter.equals( "" ))
             {
                this.toString += " (-" + commandLineCharacter + ")";
@@ -348,8 +349,8 @@ public class SetupTab implements GradleTab, GradlePluginLord.SettingsObserver {
         panel.setBorder(BorderFactory.createTitledBorder("Stack Trace Output"));
 
         showNoStackTraceRadioButton = new JRadioButton("Exceptions Only");
-        showStackTrackRadioButton = new JRadioButton("Standard Stack Trace (-" + DefaultCommandLine2StartParameterConverter.STACKTRACE + ")");  //add the command line character to the end (so if an error message says use a stack trace level, you can easily translate)
-        showFullStackTrackRadioButton = new JRadioButton("Full Stack Trace (-" + DefaultCommandLine2StartParameterConverter.FULL_STACKTRACE + ")" );
+        showStackTrackRadioButton = new JRadioButton("Standard Stack Trace (-" + DefaultCommandLineConverter.STACKTRACE + ")");  //add the command line character to the end (so if an error message says use a stack trace level, you can easily translate)
+        showFullStackTrackRadioButton = new JRadioButton("Full Stack Trace (-" + DefaultCommandLineConverter.FULL_STACKTRACE + ")" );
 
         showNoStackTraceRadioButton.putClientProperty(STACK_TRACE_LEVEL_CLIENT_PROPERTY, StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS);
         showStackTrackRadioButton.putClientProperty(STACK_TRACE_LEVEL_CLIENT_PROPERTY, StartParameter.ShowStacktrace.ALWAYS);
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineAssistantTest.groovy b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineAssistantTest.groovy
new file mode 100644
index 0000000..8b2b178
--- /dev/null
+++ b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineAssistantTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.foundation
+
+import spock.lang.Specification
+
+class CommandLineAssistantTest extends Specification {
+    def breaksUpEmptyCommandLineIntoEmptyList() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('') == []
+    }
+
+    def breaksUpWhitespaceOnlyCommandLineIntoEmptyList() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine(' \t ') == []
+    }
+
+    def breaksUpCommandLineIntoSpaceSeparatedArgument() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('a') == ['a']
+        CommandLineAssistant.breakUpCommandLine('a b\tc') == ['a', 'b', 'c']
+    }
+
+    def ignoresExtraWhiteSpaceBetweenArguments() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('  a \t') == ['a']
+        CommandLineAssistant.breakUpCommandLine('a  \t\t b ') == ['a', 'b']
+    }
+
+    def breaksUpCommandLineIntoDoubleQuotedArguments() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('"a b c"') == ['a b c']
+        CommandLineAssistant.breakUpCommandLine('a "b c d" e') == ['a', 'b c d', 'e']
+        CommandLineAssistant.breakUpCommandLine('a "  b c d  "') == ['a', '  b c d  ']
+    }
+
+    def breaksUpCommandLineIntoSingleQuotedArguments() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine("'a b c'") == ['a b c']
+        CommandLineAssistant.breakUpCommandLine("a 'b c d' e") == ['a', 'b c d', 'e']
+        CommandLineAssistant.breakUpCommandLine("a '  b c d  '") == ['a', '  b c d  ']
+    }
+
+    def canHaveEmptyQuotedArgument() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('""') == ['']
+        CommandLineAssistant.breakUpCommandLine("''") == ['']
+    }
+    
+    def canHaveQuoteInsideQuotedArgument() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('"\'quoted\'"') == ['\'quoted\'']
+        CommandLineAssistant.breakUpCommandLine("'\"quoted\"'") == ['"quoted"']
+    }
+
+    def argumentCanHaveQuotedAndUnquotedParts() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('a"b "c') == ['ab c']
+        CommandLineAssistant.breakUpCommandLine("a'b 'c") == ['ab c']
+    }
+
+    def canHaveMissingEndQuote() {
+        expect:
+        CommandLineAssistant.breakUpCommandLine('"a b c') == ['a b c']
+    }
+}
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineParsingTest.java b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineParsingTest.java
index c1b3eca..5f4e06f 100644
--- a/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineParsingTest.java
+++ b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/CommandLineParsingTest.java
@@ -16,7 +16,7 @@
 package org.gradle.foundation;
 
 import junit.framework.TestCase;
-import org.gradle.initialization.DefaultCommandLine2StartParameterConverter;
+import org.gradle.initialization.DefaultCommandLineConverter;
 
 /**
  * This tests aspects of command line parsing that the UI does.
@@ -33,24 +33,24 @@ public class CommandLineParsingTest extends TestCase {
      */
     public void testOverridingLogLevel() {
         //first try it with the log level at the end
-        String commandLine = ":build:something -" + DefaultCommandLine2StartParameterConverter.DEBUG;
+        String commandLine = ":build:something -d";
 
         CommandLineAssistant commandLineAssistant = new CommandLineAssistant();
         String[] arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasLogLevelDefined(arguments));
 
         //now try it with the log level in the middle
-        commandLine = ":build:something -" + DefaultCommandLine2StartParameterConverter.DEBUG + " :clean";
+        commandLine = ":build:something -d :clean";
         arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasLogLevelDefined(arguments));
 
         //now try it with the log level at the beginning
-        commandLine = "-" + DefaultCommandLine2StartParameterConverter.DEBUG + " :clean";
+        commandLine = "-d :clean";
         arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasLogLevelDefined(arguments));
 
         //now try it with 'info' instead of debug
-        commandLine = "-" + DefaultCommandLine2StartParameterConverter.INFO + " :clean";
+        commandLine = "-i :clean";
         arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasLogLevelDefined(arguments));
 
@@ -70,24 +70,24 @@ public class CommandLineParsingTest extends TestCase {
      */
     public void testOverridingStackTraceLevel() {
         //first try it with the stack trace level at the end
-        String commandLine = ":build:something -" + DefaultCommandLine2StartParameterConverter.FULL_STACKTRACE;
+        String commandLine = ":build:something -" + DefaultCommandLineConverter.FULL_STACKTRACE;
 
         CommandLineAssistant commandLineAssistant = new CommandLineAssistant();
         String[] arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasShowStacktraceDefined(arguments));
 
         //now try it with the stack trace level in the middle
-        commandLine = ":build:something -" + DefaultCommandLine2StartParameterConverter.FULL_STACKTRACE + " :clean";
+        commandLine = ":build:something -" + DefaultCommandLineConverter.FULL_STACKTRACE + " :clean";
         arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasShowStacktraceDefined(arguments));
 
         //now try it with the stack trace level at the beginning
-        commandLine = "-" + DefaultCommandLine2StartParameterConverter.FULL_STACKTRACE + " :clean";
+        commandLine = "-" + DefaultCommandLineConverter.FULL_STACKTRACE + " :clean";
         arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasShowStacktraceDefined(arguments));
 
         //now try it with a different stack trace level
-        commandLine = "-" + DefaultCommandLine2StartParameterConverter.STACKTRACE + " :clean";
+        commandLine = "-" + DefaultCommandLineConverter.STACKTRACE + " :clean";
         arguments = CommandLineAssistant.breakUpCommandLine(commandLine);
         assertTrue(commandLineAssistant.hasShowStacktraceDefined(arguments));
 
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy
new file mode 100644
index 0000000..d534bc5
--- /dev/null
+++ b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/ProjectConverterTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.gradle.foundation
+
+import spock.lang.Specification
+import org.gradle.api.Project
+import org.gradle.api.tasks.TaskContainer
+import org.gradle.api.Task
+
+class ProjectConverterTest extends Specification {
+    private final ProjectConverter converter = new ProjectConverter()
+
+    def convertsProjectHierarchy() {
+        Project child1 = project('child1')
+        Project child2 = project('child2')
+        Project root = project('root', child1, child2)
+        _ * root.description >> 'description'
+        _ * child1.description >> 'child1 description'
+
+        expect:
+        def views = converter.convertProjects(root)
+
+        views.size() == 1
+        def rootView = views[0]
+        rootView.name == 'root'
+        rootView.description == 'description'
+        rootView.subProjects.size() == 2
+
+        def child1View = rootView.subProjects[0]
+        child1View.name == 'child1'
+        child1View.description == 'child1 description'
+        child1View.parentProject == rootView
+        child1View.subProjects.isEmpty()
+
+        def child2View = rootView.subProjects[1]
+        child2View.name == 'child2'
+        child2View.description == ''
+        child2View.parentProject == rootView
+        child2View.subProjects.isEmpty()
+    }
+
+    def convertsTasks() {
+        Task task1 = task('t1')
+        _ * task1.description >> 't1 description'
+        Task task2 = task('t2')
+        Project root = project('root', task1, task2)
+
+        expect:
+        def views = converter.convertProjects(root)
+        def rootView = views[0]
+        rootView.tasks.size() == 2
+
+        def task1View = rootView.tasks[0]
+        task1View.name == 't1'
+        task1View.description == 't1 description'
+        task1View.project == rootView
+
+        def task2View = rootView.tasks[1]
+        task2View.name == 't2'
+        task2View.description == ''
+        task2View.project == rootView
+    }
+
+    def task(String name) {
+        Task task = Mock()
+        _ * task.name >> name
+        return task
+    }
+
+    def project(String name) {
+        project(name, [], [])
+    }
+
+    def project(String name, Project... subprojects) {
+        project(name, [], subprojects as List)
+    }
+
+    def project(String name, Task... tasks) {
+        project(name, tasks as List, [])
+    }
+
+    def project(String name, Collection<Task> tasks, Collection<Project> subprojects) {
+        Project project = Mock()
+        TaskContainer taskContainer = Mock()
+        _ * project.name >> name
+        _ * project.tasks >> taskContainer
+        _ * project.defaultTasks >> []
+        _ * taskContainer.iterator() >> tasks.iterator()
+        _ * project.dependsOnProjects >> []
+        _ * project.childProjects >> subprojects.inject([:]) { v, p -> v[p.name] = p; v }
+        return project
+    }
+}
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/TestUtility.java b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/TestUtility.java
index 778fbb2..96b1512 100644
--- a/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/TestUtility.java
+++ b/subprojects/gradle-ui/src/test/groovy/org/gradle/foundation/TestUtility.java
@@ -26,12 +26,15 @@ import org.gradle.gradleplugin.foundation.GradlePluginLord;
 import org.gradle.gradleplugin.foundation.request.ExecutionRequest;
 import org.gradle.gradleplugin.foundation.request.RefreshTaskListRequest;
 import org.gradle.gradleplugin.foundation.request.Request;
+import org.gradle.util.UncheckedException;
 import org.jmock.Expectations;
 import org.jmock.integration.junit4.JUnit4Mockery;
 
 import javax.swing.filechooser.FileFilter;
 import java.io.File;
 import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -53,6 +56,8 @@ public class TestUtility {
         context.checking(new Expectations() {{
             allowing(project).getName();
             will(returnValue(name));
+            allowing(project).getDescription();
+            will(returnValue(null));
             allowing(project).getBuildFile();
             will(returnValue(new File(buildFilePath)));
             allowing(project).getDepth();
@@ -77,15 +82,10 @@ public class TestUtility {
      * empty array to set no sub projects.
     */
     public static void attachSubProjects(JUnit4Mockery context, final Project parentProject, Project... subProjectArray) {
-        final Set<Project> set = new LinkedHashSet<Project>();   //using a LinkedHashSet rather than TreeSet (which is what gradle uses) so I don't have to deal with compareTo() being called on mock objects.
-
-        if (subProjectArray != null && subProjectArray.length != 0) {
-            set.addAll(Arrays.asList(subProjectArray));
-
-            //set the parent project of the sub projects
-            for (int index = 0; index < subProjectArray.length; index++) {
-                final Project subProject = subProjectArray[index];
-
+        final Map<String, Project> childProjects = new LinkedHashMap<String, Project>();
+        if (subProjectArray != null) {
+            for (final Project subProject : subProjectArray) {
+                childProjects.put(String.valueOf(childProjects.size()), subProject);
                 context.checking(new Expectations() {{
                     allowing(subProject).getParent();
                     will(returnValue(parentProject));
@@ -95,8 +95,8 @@ public class TestUtility {
 
         //populate the subprojects (this may be an empty set)
         context.checking(new Expectations() {{
-            allowing(parentProject).getSubprojects();
-            will(returnValue(set));
+            allowing(parentProject).getChildProjects();
+            will(returnValue(childProjects));
         }});
     }
 
@@ -152,8 +152,8 @@ public class TestUtility {
 
         //populate the task container (this may be an empty set)
         context.checking(new Expectations() {{
-            allowing(taskContainer).getAll();
-            will(returnValue(set));
+            allowing(taskContainer).iterator();
+            will(returnIterator(set));
         }});
     }
 
@@ -285,7 +285,7 @@ public class TestUtility {
     }
 
     /**
-     * This is an ImportInteraction implemention meant to be used by tests. See TestExportInteraction for more
+     * This is an ImportInteraction implementation meant to be used by tests. See TestExportInteraction for more
      * information.
     */
     public static class TestImportInteraction implements DOM4JSerializer.ImportInteraction {
@@ -316,19 +316,12 @@ public class TestUtility {
      * This refreshes the projects but blocks until it is complete (its being executed in a separate process).
      *
      * @param gradlePluginLord the plugin lord (will be used to execute the command and store the results).
-     * @param maximumWaitSeconds how many seconds to wait before considering this a failure.
     */
-    public static void refreshProjectsBlocking(GradlePluginLord gradlePluginLord, int maximumWaitSeconds) {
+    public static void refreshProjectsBlocking(GradlePluginLord gradlePluginLord, int maximumWaitValue, TimeUnit maximumWaitUnits) {
         refreshProjectsBlocking(gradlePluginLord, new ExecuteGradleCommandServerProtocol.ExecutionInteraction() {
             public void reportExecutionStarted() {
             }
 
-           /**
-            Notification of the total number of tasks that will be executed. This is
-            called after reportExecutionStarted and before any tasks are executed.
-
-            @param size the total number of tasks.
-            */
            public void reportNumberOfTasksToExecute( int size ) {
            }
 
@@ -343,13 +336,13 @@ public class TestUtility {
 
             public void reportLiveOutput(String message) {
             }
-        }, maximumWaitSeconds);
+        }, maximumWaitValue, maximumWaitUnits);
     }
 
-    public static void refreshProjectsBlocking(GradlePluginLord gradlePluginLord, final ExecuteGradleCommandServerProtocol.ExecutionInteraction executionInteraction, int maximumWaitSeconds) {
+    private static void refreshProjectsBlocking(GradlePluginLord gradlePluginLord, final ExecuteGradleCommandServerProtocol.ExecutionInteraction executionInteraction, int maximumWaitValue, TimeUnit maximumWaitUnits) {
         gradlePluginLord.startExecutionQueue();   //make sure its started
 
-        final AtomicBoolean isComplete = new AtomicBoolean();
+        final CountDownLatch complete = new CountDownLatch(1);
 
         GradlePluginLord.RequestObserver observer = new GradlePluginLord.RequestObserver() {
            public void executionRequestAdded( ExecutionRequest request ) {}
@@ -360,7 +353,7 @@ public class TestUtility {
            public void aboutToExecuteRequest( Request request ) { }
 
            public void requestExecutionComplete( Request request, int result, String output ) {
-               isComplete.set(true);
+               complete.countDown();
            }
         };
 
@@ -370,25 +363,20 @@ public class TestUtility {
         //make sure we've got a request
         Assert.assertNotNull(request);
 
-        //now sleep until we're complete, but bail if we wait too long
-        int totalWaitTime = 0;
-        while (!isComplete.get() && totalWaitTime <= maximumWaitSeconds) {
-            try {
-                Thread.sleep(1000);
-            }
-            catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-
-            totalWaitTime += 1;
+        //now wait until we're complete, but bail if we wait too long
+        boolean completed;
+        try {
+            completed = complete.await(maximumWaitValue, maximumWaitUnits);
+        } catch (InterruptedException e) {
+            throw UncheckedException.asUncheckedException(e);
         }
 
         gradlePluginLord.removeRequestObserver( observer );
 
-        if (!isComplete.get()) //its still running. Something is wrong.
+        if (!completed) //its still running. Something is wrong.
         {
             request.cancel(); //just to clean up after ourselves a little, cancel the request.
-            throw new AssertionFailedError("Failed to complete refresh in alotted time: " + maximumWaitSeconds + " seconds. Considering this failed.");
+            throw new AssertionFailedError("Failed to complete refresh in alotted time: " + maximumWaitValue + " " + maximumWaitUnits + ". Considering this failed.");
         }
     }
 
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy b/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
deleted file mode 100644
index 6782090..0000000
--- a/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/LiveOutputIntegrationTest.groovy
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-package org.gradle.integtests
-
-import org.gradle.foundation.TestUtility
-import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol
-import org.gradle.gradleplugin.foundation.GradlePluginLord
-import org.gradle.gradleplugin.foundation.runner.GradleRunner
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
-This tests the that live output is gathered while executing a task.
- at author mhunsicker
-*/
- at RunWith(DistributionIntegrationTestRunner.class)
-class LiveOutputIntegrationTest {
-
-    static final String JAVA_PROJECT_NAME = 'javaproject'
-    static final String SHARED_NAME = 'shared'
-    static final String API_NAME = 'api'
-    static final String WEBAPP_NAME = 'webservice'
-    static final String SERVICES_NAME = 'services'
-    static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
-    private File javaprojectDir
-
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('java/quickstart')
-
-    @Before
-    void setUp() {
-        javaprojectDir = sample.dir
-    }
-
-    /**
-This executes 'build' on the java multiproject sample. We want to make sure that
-we do get live output from gradle. We're not concerned with what it is, because
-that's likely to change over time. This version executes the command via GradlePlugin.
-
- at author mhunsicker
-*/
-
-    @Test
-    public void liveOutputObtainedViaGradlePlugin() {
-       System.out.println("project dir: " + javaprojectDir );
-        // Build and test projects
-        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
-
-        File multiProjectDirectory = sample.getDir();
-        Assert.assertTrue(multiProjectDirectory.exists()); //make sure things are setup the way we expect
-
-        GradlePluginLord gradlePluginLord = new GradlePluginLord();
-        gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
-        gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
-
-        gradlePluginLord.startExecutionQueue(); //for tests, we'll need to explicitly start the execution queue (unless we do a refresh via the TestUtility).
-
-        TestExecutionInteraction executionInteraction = new TestExecutionInteraction();
-
-        //execute a command. We don't really care what the command is, just something that generates output
-        TestUtility.executeBlocking( gradlePluginLord, "-t", "Test Execution", executionInteraction, 45 )
-
-        verifyLiveOutputObtained( executionInteraction );
-    }
-
-    /**
-This executes 'build' on the java multiproject sample. We want to make sure that
-we do get live output from gradle. We're not concerned with what it is, because
-that's likely to change over time. This version executes the command via GradleRunner.
-
- at author mhunsicker
-*/
-    @Test
-    public void liveOutputObtainedViaGradleRunner() {
-        // Build and test projects
-        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
-
-        File multiProjectDirectory = sample.getDir();
-        Assert.assertTrue(multiProjectDirectory.exists()); //make sure things are setup the way we expect
-
-        GradleRunner gradleRunner = new GradleRunner( multiProjectDirectory, dist.gradleHomeDir, null );
-
-        TestExecutionInteraction executionInteraction = new TestExecutionInteraction();
-
-        //execute a command. We don't really care what the command is, just something that generates output
-        gradleRunner.executeCommand("-t", org.gradle.api.logging.LogLevel.LIFECYCLE,
-                                            org.gradle.StartParameter.ShowStacktrace.INTERNAL_EXCEPTIONS,
-                                            executionInteraction);
-
-        //now sleep until we're complete, but bail if we wait too long
-        int maximumWaitSeconds = 45; //this is totally arbitrary and is worse case senario.
-        int totalWaitTime = 0;
-        while( !executionInteraction.executionFinishedReported && totalWaitTime <= maximumWaitSeconds) {
-            try {
-               println "Waiting. Has Finished: " + executionInteraction.executionFinishedReported + ". Total Time: "+ totalWaitTime
-                Thread.sleep(1000);
-            }
-            catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-
-            totalWaitTime += 1;
-        }
-
-        verifyLiveOutputObtained( executionInteraction );
-    }
-
-
-
-   /**
-  This verifies that it has live output. It also checks that we received some final output as well
-  as that the execution was successful
-  */
-   private void verifyLiveOutputObtained( TestExecutionInteraction executionInteraction )
-   {
-      //Make sure we were successful. If we weren't successful, that probably indicates a different problem and the test itself may be invalid.
-      Assert.assertTrue( "Verifying execution was successful failed", executionInteraction.wasSuccessful )
-
-      //verify that we actually finished. If not, then we timed out, which may mean the machine is really slow or that there's a serious problem.
-      Assert.assertTrue( "Verifying execution finished in a timely manner", executionInteraction.executionFinishedReported );
-
-      //make sure we received some output! I just made up 30 because I wanted more than just 1 character and there should actually be dozens of characters.
-      Assert.assertTrue( "Verifying live output was obtained", executionInteraction.liveOutput.length() >= 30 )
-
-      //We should also get a final message. Note: this is usually a little different from the live output, if for not other reason than
-      //timing issues of when the last live output is sent. The final message should have everything, but we might not get the last
-      //live output. As such, we won't verify they're equal.
-      Assert.assertTrue( "Verifying the final output message was received", executionInteraction.finalMessage.length() > 30 )
-   }
-}
-
-//this class just holds onto our liveOutput and also tracks whether or not we've finished.
-public class TestExecutionInteraction implements ExecuteGradleCommandServerProtocol.ExecutionInteraction
-{
-   public StringBuilder liveOutput = new StringBuilder();
-   public boolean executionFinishedReported = false;
-   public boolean wasSuccessful = false;
-   public String finalMessage;
-
-   public void reportLiveOutput(String message)
-   {
-      liveOutput.append( message );
-   }
-
-   //when we finish executing, we'll make sure we got some type of live output from gradle.
-   public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable)
-   {
-      println "Received execution finished"
-      executionFinishedReported = true
-      this.wasSuccessful = true
-      this.finalMessage = message
-   }
-
-   public void reportExecutionStarted() { }
-   public void reportNumberOfTasksToExecute(int size) { }
-   public void reportTaskStarted(String message, float percentComplete) { }
-   public void reportTaskComplete(String message, float percentComplete) { }
-
-
-
-}
\ No newline at end of file
diff --git a/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy b/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
deleted file mode 100644
index 1f2c46e..0000000
--- a/subprojects/gradle-ui/src/test/groovy/org/gradle/integtests/MultiprojectProjectAndTaskListIntegrationTest.groovy
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright 2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.gradle.integtests
-
-import org.gradle.foundation.ProjectView
-import org.gradle.foundation.TaskView
-import org.gradle.gradleplugin.foundation.GradlePluginLord
-import org.gradle.integtests.fixtures.GradleDistribution
-import org.gradle.integtests.fixtures.GradleDistributionExecuter
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.openapi.external.foundation.ProjectVersion1
-import org.gradle.openapi.wrappers.foundation.GradleInterfaceWrapperVersion1
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- This tests the multiproject sample with the GradleView mechanism.
- @author mhunsicker
- */
- at RunWith(DistributionIntegrationTestRunner.class)
-class MultiprojectProjectAndTaskListIntegrationTest {
-
-    static final String JAVA_PROJECT_NAME = 'javaproject'
-    static final String SHARED_NAME = 'shared'
-    static final String API_NAME = 'api'
-    static final String WEBAPP_NAME = 'webservice'
-    static final String SERVICES_NAME = 'services'
-    static final String WEBAPP_PATH = "$SERVICES_NAME/$WEBAPP_NAME" as String
-
-    private File javaprojectDir
-
-    @Rule public final GradleDistribution dist = new GradleDistribution()
-    @Rule public final GradleDistributionExecuter executer = new GradleDistributionExecuter()
-    @Rule public final Sample sample = new Sample('java/quickstart')
-
-    @Before
-    void setUp() {
-        javaprojectDir = sample.getDir()
-    }
-
-    /*
-       This tests against the multiproject sample. It expects to find not just
-       the root level projects, but also the nested sub projects
-       (services:webservice). This isn't really interested in the actual tasks
-       themselves (I fear those may change too often to worry with keeping the
-       test up to date).
-
-       @author mhunsicker
-    */
-
-    @Test
-    public void multiProjectjavaProjectSample() {
-        // Build and test projects
-        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
-
-        File multiProjectDirectory = sample.getDir();
-        Assert.assertTrue(multiProjectDirectory.exists());
-
-        GradlePluginLord gradlePluginLord = new GradlePluginLord();
-        gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
-        gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
-
-        //refresh the projects and wait. This will throw an exception if it fails.
-        org.gradle.foundation.TestUtility.refreshProjectsBlocking(gradlePluginLord, 20);  //we'll give this 20 seconds to complete
-
-        //get the root project
-        List<ProjectView> projects = gradlePluginLord.getProjects();
-        Assert.assertNotNull(projects);
-
-        //make sure there weren't other root projects found.
-        Assert.assertEquals(1, projects.size());
-
-        ProjectView rootProject = projects.get(0);
-        Assert.assertNotNull(rootProject);
-        Assert.assertEquals("multiproject", rootProject.getName());
-
-        //now check for sub projects, api, shared, and services
-        ProjectView apiProject = rootProject.getSubProject("api");
-        Assert.assertNotNull(apiProject);
-        Assert.assertTrue(apiProject.getSubProjects().isEmpty());  //this has no sub projects
-
-        ProjectView sharedProject = rootProject.getSubProject("shared");
-        Assert.assertNotNull(sharedProject);
-        Assert.assertTrue(sharedProject.getSubProjects().isEmpty());  //this has no sub projects
-
-        ProjectView servicesProject = rootProject.getSubProject("services");
-        Assert.assertNotNull(servicesProject);
-
-        //services has a sub project
-        ProjectView webservicesProject = servicesProject.getSubProject("webservice");
-        Assert.assertNotNull(webservicesProject);
-        Assert.assertTrue(webservicesProject.getSubProjects().isEmpty());  //this has no sub projects
-
-        //make sure we didn't inadvertantly find other sub projects.
-        Assert.assertEquals(3, rootProject.getSubProjects().size());
-    }
-
-   /**
-    This tests that the wrappers for projects and tasks are working
-    */
-   @Test
-   public void testOpenAPIWrapperProjectAndTaskList()
-   {
-     // Build and test projects
-        executer.inDirectory(javaprojectDir).withTasks('assemble').run();
-
-        File multiProjectDirectory = sample.getDir();
-        Assert.assertTrue(multiProjectDirectory.exists());
-
-        GradlePluginLord gradlePluginLord = new GradlePluginLord();
-        gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
-        gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
-
-        GradleInterfaceWrapperVersion1 wrapper = new GradleInterfaceWrapperVersion1( gradlePluginLord );
-
-        //the rest of this uses the open API mechanism to access the projects and tasks
-
-        //refresh the projects and wait. This will throw an exception if it fails.
-        org.gradle.foundation.TestUtility.refreshProjectsBlocking(gradlePluginLord, 20);  //we'll give this 20 seconds to complete
-
-        //get the root project
-        List<ProjectVersion1> projects = wrapper.getRootProjects();
-        Assert.assertNotNull(projects);
-
-        //make sure there weren't other root projects found.
-        Assert.assertEquals(1, projects.size());
-
-        ProjectVersion1 rootProject = projects.get(0);
-        Assert.assertNotNull(rootProject);
-        Assert.assertEquals("multiproject", rootProject.getName());
-
-        //now check for sub projects, api, shared, and services
-        ProjectVersion1 apiProject = rootProject.getSubProject("api");
-        Assert.assertNotNull(apiProject);
-        Assert.assertTrue(apiProject.getSubProjects().isEmpty());  //this has no sub projects
-
-        ProjectVersion1 sharedProject = rootProject.getSubProject("shared");
-        Assert.assertNotNull(sharedProject);
-        Assert.assertTrue(sharedProject.getSubProjects().isEmpty());  //this has no sub projects
-
-        ProjectVersion1 servicesProject = rootProject.getSubProject("services");
-        Assert.assertNotNull(servicesProject);
-
-        //services has a sub project
-        ProjectVersion1 webservicesProject = servicesProject.getSubProject("webservice");
-        Assert.assertNotNull(webservicesProject);
-        Assert.assertTrue(webservicesProject.getSubProjects().isEmpty());  //this has no sub projects
-
-        //make sure we didn't inadvertantly find other sub projects.
-        Assert.assertEquals(3, rootProject.getSubProjects().size());
-
-        //I don't want to keep the actual tasks in synch, but let's make sure there's something there.
-        def tasks = apiProject.getTasks()
-        Assert.assertNotNull( tasks );
-        Assert.assertFalse( tasks.isEmpty() );
-   }
-
-   /**
-   * This tests ProjectView.getSubProjectFromFullPath. Specifically, the first character
-    * is optionally a colon. So this tests it both ways.
-   */
-   @Test
-   public void testSubProjectFromFullPath()
-   {
-     executer.inDirectory(javaprojectDir).withTasks('assemble').run();
-
-      File multiProjectDirectory = sample.getDir();
-      Assert.assertTrue(multiProjectDirectory.exists());
-
-      GradlePluginLord gradlePluginLord = new GradlePluginLord();
-      gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
-      gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
-
-      //refresh the projects and wait. This will throw an exception if it fails.
-      org.gradle.foundation.TestUtility.refreshProjectsBlocking(gradlePluginLord, 20);  //we'll give this 20 seconds to complete
-
-      //get the root project
-      List<ProjectView> projects = gradlePluginLord.getProjects();
-      Assert.assertNotNull(projects);
-      Assert.assertFalse( projects.isEmpty() );
-
-      ProjectView rootProject = projects.get(0)
-
-      //test it using no prefixed colon
-      ProjectView foundProject1 = rootProject.getSubProjectFromFullPath("services:webservice")
-      Assert.assertNotNull( foundProject1 )
-
-      //test it using a prefixed colon
-      ProjectView foundProject2 = rootProject.getSubProjectFromFullPath(":services:webservice")
-      Assert.assertNotNull( foundProject2 )
-
-      //should both the same project
-      Assert.assertEquals( foundProject1, foundProject2 )
-   }
-
-   /**
-   * This tests TaskView.getTaskFromFullPath. Specifically, the first character
-    * is optionally a colon. So this tests it both ways.
-   */
-   @Test
-   public void testGetTaskFromFullPath()
-   {
-     executer.inDirectory(javaprojectDir).withTasks('assemble').run();
-
-      File multiProjectDirectory = sample.getDir();
-      Assert.assertTrue(multiProjectDirectory.exists());
-
-      GradlePluginLord gradlePluginLord = new GradlePluginLord();
-      gradlePluginLord.setCurrentDirectory(multiProjectDirectory);
-      gradlePluginLord.setGradleHomeDirectory(dist.gradleHomeDir);
-
-      //refresh the projects and wait. This will throw an exception if it fails.
-      org.gradle.foundation.TestUtility.refreshProjectsBlocking(gradlePluginLord, 20);  //we'll give this 20 seconds to complete
-
-      //get the root project
-      List<ProjectView> projects = gradlePluginLord.getProjects();
-      Assert.assertNotNull(projects);
-      Assert.assertFalse( projects.isEmpty() );
-
-      ProjectView rootProject = projects.get(0)
-
-      //test it using no prefixed colon
-      TaskView foundTask1 = rootProject.getTaskFromFullPath("api:build")
-      Assert.assertNotNull( foundTask1 )
-
-      //test it using a prefixed colon
-      TaskView foundTask2 = rootProject.getTaskFromFullPath(":api:build")
-      Assert.assertNotNull( foundTask2 )
-
-      //should both the same project
-      Assert.assertEquals( foundTask1, foundTask2 )      
-   }
-}
\ No newline at end of file
diff --git a/subprojects/gradle-ui/ui.gradle b/subprojects/gradle-ui/ui.gradle
index acf4e76..81efd51 100644
--- a/subprojects/gradle-ui/ui.gradle
+++ b/subprojects/gradle-ui/ui.gradle
@@ -13,6 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+apply from: "$rootDir/gradle/integTest.gradle"
+
 dependencies {
     compile project(':core')
     compile project(':openApi')
@@ -21,33 +24,25 @@ dependencies {
 
     compile libraries.dom4j,
             libraries.commons_io,
-            libraries.slf4j_api,
-            libraries.jopt_simple
+            libraries.slf4j_api
 
     runtime libraries.jaxen
 
     testCompile project(path: ':core', configuration: 'testFixtures')
-    testCompile project(path: ':core', configuration: 'integTestFixtures')
     testRuntime project(path: ':core', configuration: 'testFixturesRuntime')
-    testRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
+    integTestCompile project(path: ':core', configuration: 'integTestFixtures')
+    integTestRuntime project(path: ':core', configuration: 'integTestFixturesRuntime')
 }
 
 task integTest(type: Test, dependsOn: [ rootProject.intTestImage ]) {
     systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath
-    systemProperties['integTest.srcDir'] = file('src').absolutePath
     systemProperties['integTest.gradleUserHomeDir'] = rootProject.integTest.integTestUserDir.absolutePath
-    include 'org/gradle/integtests/**/*IntegrationTest.*'
-    testClassesDir = sourceSets.test.classesDir
-    classpath = sourceSets.test.runtimeClasspath
+    testClassesDir = sourceSets.integTest.classesDir
+    classpath = sourceSets.integTest.runtimeClasspath
 
     doFirst { task -> systemProperties['integTest.gradleHomeDir'] = rootProject.intTestImage.integTestGradleHome.absolutePath }
 }
 
-compileJava.options.depend()
-
-//this is just to exclude integration tests
 test {
     jvmArgs '-Xms128m', '-Xmx256m', '-XX:MaxPermSize=128m', '-XX:+HeapDumpOnOutOfMemoryError'
-
-    exclude 'org/gradle/integtests/**/*.*'
 }
diff --git a/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java b/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
index c8ab291..0b84100 100644
--- a/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
+++ b/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
@@ -20,6 +20,7 @@ import org.gradle.api.DefaultTask;
 import org.gradle.api.GradleException;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.wrapper.internal.WrapperScriptGenerator;
 import org.gradle.util.GFileUtils;
 import org.gradle.util.GUtil;
 
@@ -28,7 +29,7 @@ import java.net.URL;
 import java.util.Properties;
 
 /**
- * The wrapper task generates scripts (for *nix and windows) which enable to build your project with Gradle, without having to install Gradle.
+ * Generates scripts (for *nix and windows) which enable to build your project with Gradle, without having to install Gradle.
  * The scripts generated by this task are supposed to be committed to your version control system. This tasks also copies
  * a gradle-wrapper.jar to your project dir which needs also be committed into your VCS.
  * The scripts delegates to this jar. If a user execute a wrapper script the first time, the script downloads the gradle-distribution and
@@ -57,6 +58,9 @@ public class Wrapper extends DefaultTask {
     public static final String DEFAULT_ARCHIVE_NAME = "gradle";
     public static final String DEFAULT_ARCHIVE_CLASSIFIER = "bin";
 
+    /**
+     * Specifies how the wrapper path should be interpreted.
+     */
     public enum PathBase { PROJECT, GRADLE_USER_HOME }
 
     @Input
@@ -219,7 +223,7 @@ public class Wrapper extends DefaultTask {
 
     /**
      * A URL where to download the gradle distribution from. The pattern used by the wrapper for downloading is:
-     * <code>{@link #getUrlRoot()}</i>/{@link #getArchiveName()}-{@link #getArchiveClassifier()}-{@link #getGradleVersion()}.zip</code>.
+     * <code>{@link #getUrlRoot()}/{@link #getArchiveName()}-{@link #getArchiveClassifier()}-{@link #getGradleVersion()}.zip</code>.
      * The default for the URL root is {@link #DEFAULT_URL_ROOT}.
      *
      * The wrapper downloads a certain distribution only ones and caches it.
@@ -303,7 +307,7 @@ public class Wrapper extends DefaultTask {
     /**
      * Specifies the name of the archive as part of the download URL. The download URL is assembled by the pattern:
      *
-     * <code>{@link #getUrlRoot()}</i>/{@link #getArchiveName()}-{@link #getArchiveClassifier()}-{@link #getGradleVersion()}.zip</code>
+     * <code>{@link #getUrlRoot()}/{@link #getArchiveName()}-{@link #getArchiveClassifier()}-{@link #getGradleVersion()}.zip</code>
      *
      * The default for the archive name is {@link #DEFAULT_ARCHIVE_NAME}.
      *
@@ -325,7 +329,7 @@ public class Wrapper extends DefaultTask {
     /**
      * Specifies the classifier of the archive as part of the download URL. The download URL is assembled by the pattern:
      *
-     * <code>{@link #getUrlRoot()}</i>/{@link #getArchiveName()}-{@link #getArchiveClassifier()}-{@link #getGradleVersion()}.zip</code>
+     * <code>{@link #getUrlRoot()}/{@link #getArchiveName()}-{@link #getArchiveClassifier()}-{@link #getGradleVersion()}.zip</code>
      *
      * The default for the archive classifier is {@link #DEFAULT_ARCHIVE_CLASSIFIER}.
      *
diff --git a/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/WrapperScriptGenerator.java b/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/WrapperScriptGenerator.java
deleted file mode 100644
index 0b77574..0000000
--- a/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/WrapperScriptGenerator.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2007 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.tasks.wrapper;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.tools.ant.taskdefs.Chmod;
-import org.gradle.api.UncheckedIOException;
-import org.gradle.util.AntUtil;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-
-/**
- * @author Hans Dockter
- */
-public class WrapperScriptGenerator {
-    public static final String UNIX_NL = "\n";
-    public static final String CURRENT_DIR_UNIX = "`dirname \"$0\"`";
-    public static final String WINDOWS_NL = "\n";
-    public static final String CURRENT_DIR_WINDOWS = "%DIRNAME%";
-    private static final String FULLY_QUALIFIED_WRAPPER_NAME = "org.gradle.wrapper.GradleWrapperMain";
-
-    public void generate(String jarPath, String wrapperPropertiesPath, File scriptDestinationDir) {
-        try {
-            createUnixScript(jarPath, scriptDestinationDir, wrapperPropertiesPath);
-            createWindowsScript(jarPath, scriptDestinationDir, wrapperPropertiesPath);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-    private void createUnixScript(String jarPath, File scriptDestinationDir, String wrapperPropertiesPath) throws IOException {
-        String unixWrapperScriptHead = IOUtils.toString(Wrapper.class.getResourceAsStream("unixWrapperScriptHead.txt"));
-        String unixWrapperScriptTail = IOUtils.toString(Wrapper.class.getResourceAsStream("unixWrapperScriptTail.txt"));
-
-        String fillingUnix = "" + UNIX_NL +
-                "STARTER_MAIN_CLASS=" + FULLY_QUALIFIED_WRAPPER_NAME + UNIX_NL +
-                "CLASSPATH=" + CURRENT_DIR_UNIX + "/" + FilenameUtils.separatorsToUnix(jarPath) + UNIX_NL +
-                "WRAPPER_PROPERTIES=" + CURRENT_DIR_UNIX + "/" + FilenameUtils.separatorsToUnix(wrapperPropertiesPath) + UNIX_NL;
-
-        String unixScript = unixWrapperScriptHead + fillingUnix + unixWrapperScriptTail;
-        File unixScriptFile = new File(scriptDestinationDir, "gradlew");
-        FileUtils.writeStringToFile(unixScriptFile, unixScript);
-        createExecutablePermission(unixScriptFile);
-    }
-
-    private void createExecutablePermission(File unixScriptFile) {
-        Chmod chmod = new Chmod();
-        chmod.setProject(AntUtil.createProject());
-        chmod.setFile(unixScriptFile);
-        chmod.setPerm("ugo+rx");
-        chmod.execute();
-    }
-
-    private void createWindowsScript(String jarPath, File scriptDestinationDir, String wrapperPropertiesPath) throws IOException {
-        String windowsWrapperScriptHead = IOUtils.toString(Wrapper.class.getResourceAsStream("windowsWrapperScriptHead.txt"));
-        String windowsWrapperScriptTail = IOUtils.toString(Wrapper.class.getResourceAsStream("windowsWrapperScriptTail.txt"));
-        String fillingWindows = "" + WINDOWS_NL +
-                "set STARTER_MAIN_CLASS=" + FULLY_QUALIFIED_WRAPPER_NAME + WINDOWS_NL +
-                "set CLASSPATH=" + CURRENT_DIR_WINDOWS + "\\" + FilenameUtils.separatorsToWindows(jarPath) + WINDOWS_NL +
-                "set WRAPPER_PROPERTIES=" + CURRENT_DIR_WINDOWS + "\\" + FilenameUtils.separatorsToWindows(wrapperPropertiesPath) + WINDOWS_NL;
-        String windowsScript = windowsWrapperScriptHead + fillingWindows + windowsWrapperScriptTail;
-        File windowsScriptFile = new File(scriptDestinationDir, "gradlew.bat");
-        FileUtils.writeStringToFile(windowsScriptFile, transformIntoWindowsNewLines(windowsScript));
-    }
-
-    private String transformIntoWindowsNewLines(String s) {
-        StringWriter writer = new StringWriter();
-        for (char c : s.toCharArray()) {
-            if (c == '\n') {
-                writer.write('\r');
-                writer.write('\n');
-            } else if (c != '\r') {
-                writer.write(c);
-            }
-        }        
-        return writer.toString();
-    }
-}
diff --git a/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/internal/WrapperScriptGenerator.java b/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/internal/WrapperScriptGenerator.java
new file mode 100644
index 0000000..3fb0e25
--- /dev/null
+++ b/subprojects/gradle-wrapper/src/main/java/org/gradle/api/tasks/wrapper/internal/WrapperScriptGenerator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.wrapper.internal;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tools.ant.taskdefs.Chmod;
+import org.gradle.api.UncheckedIOException;
+import org.gradle.util.AntUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+
+/**
+ * @author Hans Dockter
+ */
+public class WrapperScriptGenerator {
+    public static final String UNIX_NL = "\n";
+    public static final String CURRENT_DIR_UNIX = "`dirname \"$0\"`";
+    public static final String WINDOWS_NL = "\n";
+    public static final String CURRENT_DIR_WINDOWS = "%DIRNAME%";
+    private static final String FULLY_QUALIFIED_WRAPPER_NAME = "org.gradle.wrapper.GradleWrapperMain";
+
+    public void generate(String jarPath, String wrapperPropertiesPath, File scriptDestinationDir) {
+        try {
+            createUnixScript(jarPath, scriptDestinationDir, wrapperPropertiesPath);
+            createWindowsScript(jarPath, scriptDestinationDir, wrapperPropertiesPath);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private void createUnixScript(String jarPath, File scriptDestinationDir, String wrapperPropertiesPath) throws IOException {
+        String unixWrapperScriptHead = IOUtils.toString(getClass().getResourceAsStream("unixWrapperScriptHead.txt"));
+        String unixWrapperScriptTail = IOUtils.toString(getClass().getResourceAsStream("unixWrapperScriptTail.txt"));
+
+        String fillingUnix = "" + UNIX_NL +
+                "STARTER_MAIN_CLASS=" + FULLY_QUALIFIED_WRAPPER_NAME + UNIX_NL +
+                "CLASSPATH=" + CURRENT_DIR_UNIX + "/" + FilenameUtils.separatorsToUnix(jarPath) + UNIX_NL +
+                "WRAPPER_PROPERTIES=" + CURRENT_DIR_UNIX + "/" + FilenameUtils.separatorsToUnix(wrapperPropertiesPath) + UNIX_NL;
+
+        String unixScript = unixWrapperScriptHead + fillingUnix + unixWrapperScriptTail;
+        File unixScriptFile = new File(scriptDestinationDir, "gradlew");
+        FileUtils.writeStringToFile(unixScriptFile, unixScript);
+        createExecutablePermission(unixScriptFile);
+    }
+
+    private void createExecutablePermission(File unixScriptFile) {
+        Chmod chmod = new Chmod();
+        chmod.setProject(AntUtil.createProject());
+        chmod.setFile(unixScriptFile);
+        chmod.setPerm("ugo+rx");
+        chmod.execute();
+    }
+
+    private void createWindowsScript(String jarPath, File scriptDestinationDir, String wrapperPropertiesPath) throws IOException {
+        String windowsWrapperScriptHead = IOUtils.toString(getClass().getResourceAsStream("windowsWrapperScriptHead.txt"));
+        String windowsWrapperScriptTail = IOUtils.toString(getClass().getResourceAsStream("windowsWrapperScriptTail.txt"));
+        String fillingWindows = "" + WINDOWS_NL +
+                "set STARTER_MAIN_CLASS=" + FULLY_QUALIFIED_WRAPPER_NAME + WINDOWS_NL +
+                "set CLASSPATH=" + CURRENT_DIR_WINDOWS + "\\" + FilenameUtils.separatorsToWindows(jarPath) + WINDOWS_NL +
+                "set WRAPPER_PROPERTIES=" + CURRENT_DIR_WINDOWS + "\\" + FilenameUtils.separatorsToWindows(wrapperPropertiesPath) + WINDOWS_NL;
+        String windowsScript = windowsWrapperScriptHead + fillingWindows + windowsWrapperScriptTail;
+        File windowsScriptFile = new File(scriptDestinationDir, "gradlew.bat");
+        FileUtils.writeStringToFile(windowsScriptFile, transformIntoWindowsNewLines(windowsScript));
+    }
+
+    private String transformIntoWindowsNewLines(String s) {
+        StringWriter writer = new StringWriter();
+        for (char c : s.toCharArray()) {
+            if (c == '\n') {
+                writer.write('\r');
+                writer.write('\n');
+            } else if (c != '\r') {
+                writer.write(c);
+            }
+        }        
+        return writer.toString();
+    }
+}
diff --git a/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/SystemPropertiesHandler.java b/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/SystemPropertiesHandler.java
index f7f8c5c..81a5c08 100644
--- a/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/SystemPropertiesHandler.java
+++ b/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/SystemPropertiesHandler.java
@@ -51,7 +51,12 @@ public class SystemPropertiesHandler {
         }
         Properties properties = new Properties();
         try {
-            properties.load(new FileInputStream(propertiesFile));
+            FileInputStream inStream = new FileInputStream(propertiesFile);
+            try {
+                properties.load(inStream);
+            } finally {
+                inStream.close();
+            }
         } catch (IOException e) {
             throw new RuntimeException("Error when loading properties file=" + propertiesFile, e);
         }
diff --git a/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/Wrapper.java b/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/Wrapper.java
index ec8ee50..c416198 100644
--- a/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/Wrapper.java
+++ b/subprojects/gradle-wrapper/src/main/java/org/gradle/wrapper/Wrapper.java
@@ -17,6 +17,7 @@ package org.gradle.wrapper;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Properties;
 
@@ -37,7 +38,12 @@ public class Wrapper {
 
     public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
         Properties wrapperProperties = new Properties();
-        wrapperProperties.load(new FileInputStream(new File(System.getProperty(WRAPPER_PROPERTIES_PROPERTY))));
+        InputStream inStream = new FileInputStream(new File(System.getProperty(WRAPPER_PROPERTIES_PROPERTY)));
+        try {
+            wrapperProperties.load(inStream);
+        } finally {
+            inStream.close();
+        }
         if (GradleWrapperMain.isDebug()) {
             System.out.println("wrapperProperties = " + wrapperProperties);
         }
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/unixWrapperScriptHead.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/unixWrapperScriptHead.txt
new file mode 100644
index 0000000..d646f03
--- /dev/null
+++ b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/unixWrapperScriptHead.txt
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+##############################################################################
+##                                                                          ##
+##  Gradle wrapper script for UN*X                                         ##
+##                                                                          ##
+##############################################################################
+
+# Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
+# GRADLE_OPTS="$GRADLE_OPTS -Xmx512m"
+# JAVA_OPTS="$JAVA_OPTS -Xmx512m"
+
+GRADLE_APP_NAME=Gradle
+
+warn ( ) {
+    echo "${PROGNAME}: $*"
+}
+
+die ( ) {
+    warn "$*"
+    exit 1
+}
+
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set JAVA_HOME if it's not already set.
+if [ -z "$JAVA_HOME" ] ; then
+    if $darwin ; then
+        [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home"
+        [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
+    else
+        javaExecutable="`which javac`"
+        [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME."
+        # readlink(1) is not available as standard on Solaris 10.
+        readLink=`which readlink`
+        [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME."
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+        export JAVA_HOME="$javaHome"
+    fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"`
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/unixWrapperScriptTail.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/unixWrapperScriptTail.txt
new file mode 100644
index 0000000..5ef1777
--- /dev/null
+++ b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/unixWrapperScriptTail.txt
@@ -0,0 +1,78 @@
+# Determine the Java command to use to start the JVM.
+if [ -z "$JAVACMD" ] ; then
+    if [ -n "$JAVA_HOME" ] ; then
+        if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+            # IBM's JDK on AIX uses strange locations for the executables
+            JAVACMD="$JAVA_HOME/jre/sh/java"
+        else
+            JAVACMD="$JAVA_HOME/bin/java"
+        fi
+    else
+        JAVACMD="java"
+    fi
+fi
+if [ ! -x "$JAVACMD" ] ; then
+    die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD"
+fi
+if [ -z "$JAVA_HOME" ] ; then
+    warn "JAVA_HOME environment variable is not set"
+fi
+
+# For Darwin, add GRADLE_APP_NAME to the JAVA_OPTS as -Xdock:name
+if $darwin; then
+    JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GRADLE_APP_NAME"
+# we may also want to set -Xdock:image
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done 
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+GRADLE_APP_BASE_NAME=`basename "$0"`
+
+"$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \
+        -classpath "$CLASSPATH" \
+        -Dorg.gradle.appname="$GRADLE_APP_BASE_NAME" \
+        -Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \
+        $STARTER_MAIN_CLASS \
+        "$@"
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/windowsWrapperScriptHead.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/windowsWrapperScriptHead.txt
new file mode 100644
index 0000000..e6be5b6
--- /dev/null
+++ b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/windowsWrapperScriptHead.txt
@@ -0,0 +1,101 @@
+ at if "%DEBUG%" == "" @echo off
+ at rem ##########################################################################
+ at rem                                                                         ##
+ at rem  Gradle startup script for Windows                                      ##
+ at rem                                                                         ##
+ at rem ##########################################################################
+
+ at rem
+ at rem $Revision: 10602 $ $Date: 2008-01-25 02:49:54 +0100 (ven., 25 janv. 2008) $
+ at rem
+
+ at rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+ at rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
+ at rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512m
+ at rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+ at rem Determine the command interpreter to execute the "CD" later
+set COMMAND_COM="cmd.exe"
+if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
+if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
+
+ at rem Use explicit find.exe to prevent cygwin and others find.exe from being used
+set FIND_EXE="find.exe"
+if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
+if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
+
+:check_JAVA_HOME
+ at rem Make sure we have a valid JAVA_HOME
+if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
+
+echo.
+echo ERROR: Environment variable JAVA_HOME has not been set.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+echo.
+goto end
+
+:have_JAVA_HOME
+ at rem Validate JAVA_HOME
+%COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
+if not errorlevel 1 goto init
+
+echo.
+echo ERROR: JAVA_HOME might be set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation if there are problems.
+echo.
+
+:init
+ at rem get name of script to launch with full path
+ at rem Get command-line arguments, handling Windowz variants
+SET _marker=%JAVA_HOME: =%
+ at rem IF NOT "%_marker%" == "%JAVA_HOME%" ECHO JAVA_HOME "%JAVA_HOME%" contains spaces. Please change to a location without spaces if this causes problems.
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%eval[2+2]" == "4" goto 4NT_args
+
+IF "%_marker%" == "%JAVA_HOME%" goto :win9xME_args
+
+set _FIXPATH=
+call :fixpath "%JAVA_HOME%"
+set JAVA_HOME=%_FIXPATH:~1%
+
+goto win9xME_args
+
+:fixpath
+if not %1.==. (
+for /f "tokens=1* delims=;" %%a in (%1) do (
+call :shortfilename "%%a" & call :fixpath "%%b"
+)
+)
+goto :EOF
+:shortfilename
+for %%i in (%1) do set _FIXPATH=%_FIXPATH%;%%~fsi
+goto :EOF
+
+
+:win9xME_args
+ at rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+ at rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+ at rem Setup the command line
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/windowsWrapperScriptTail.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/windowsWrapperScriptTail.txt
similarity index 100%
rename from subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/windowsWrapperScriptTail.txt
rename to subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/internal/windowsWrapperScriptTail.txt
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/unixWrapperScriptHead.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/unixWrapperScriptHead.txt
deleted file mode 100644
index 4ff9ca8..0000000
--- a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/unixWrapperScriptHead.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/bash
-
-##############################################################################
-##                                                                          ##
-##  Gradle wrapper script for UN*X                                         ##
-##                                                                          ##
-##############################################################################
-
-# Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
-# GRADLE_OPTS="$GRADLE_OPTS -Xmx512"
-# JAVA_OPTS="$JAVA_OPTS -Xmx512"
-
-GRADLE_APP_NAME=Gradle
-
-warn ( ) {
-    echo "${PROGNAME}: $*"
-}
-
-die ( ) {
-    warn "$*"
-    exit 1
-}
-
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-esac
-
-# Attempt to set JAVA_HOME if it's not already set.
-if [ -z "$JAVA_HOME" ] ; then
-    if $darwin ; then
-        [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home"
-        [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
-    else
-        javaExecutable="`which javac`"
-        [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME."
-        # readlink(1) is not available as standard on Solaris 10.
-        readLink=`which readlink`
-        [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME."
-        javaExecutable="`readlink -f \"$javaExecutable\"`"
-        javaHome="`dirname \"$javaExecutable\"`"
-        javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-        export JAVA_HOME="$javaHome"
-    fi
-fi
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
-    [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"`
-    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/unixWrapperScriptTail.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/unixWrapperScriptTail.txt
deleted file mode 100644
index d2349c9..0000000
--- a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/unixWrapperScriptTail.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-# Determine the Java command to use to start the JVM.
-if [ -z "$JAVACMD" ] ; then
-    if [ -n "$JAVA_HOME" ] ; then
-        if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-            # IBM's JDK on AIX uses strange locations for the executables
-            JAVACMD="$JAVA_HOME/jre/sh/java"
-        else
-            JAVACMD="$JAVA_HOME/bin/java"
-        fi
-    else
-        JAVACMD="java"
-    fi
-fi
-if [ ! -x "$JAVACMD" ] ; then
-    die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD"
-fi
-if [ -z "$JAVA_HOME" ] ; then
-    warn "JAVA_HOME environment variable is not set"
-fi
-
-# For Darwin, add GRADLE_APP_NAME to the JAVA_OPTS as -Xdock:name
-if $darwin; then
-    JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GRADLE_APP_NAME"
-# we may also want to set -Xdock:image
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
-    JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
-        fi
-        i=$((i+1))
-    done 
-    case $i in
-        (0) set -- ;;
-        (1) set -- "$args0" ;;
-        (2) set -- "$args0" "$args1" ;;
-        (3) set -- "$args0" "$args1" "$args2" ;;
-        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
-fi
-
-"$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \
-        -classpath "$CLASSPATH" \
-        -Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \
-        $STARTER_MAIN_CLASS \
-        "$@"
diff --git a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/windowsWrapperScriptHead.txt b/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/windowsWrapperScriptHead.txt
deleted file mode 100644
index 879d397..0000000
--- a/subprojects/gradle-wrapper/src/main/resources/org/gradle/api/tasks/wrapper/windowsWrapperScriptHead.txt
+++ /dev/null
@@ -1,101 +0,0 @@
- at if "%DEBUG%" == "" @echo off
- at rem ##########################################################################
- at rem                                                                         ##
- at rem  Gradle startup script for Windows                                      ##
- at rem                                                                         ##
- at rem ##########################################################################
-
- at rem
- at rem $Revision: 10602 $ $Date: 2008-01-25 02:49:54 +0100 (ven., 25 janv. 2008) $
- at rem
-
- at rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
- at rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together.
- at rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512
- at rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.\
-
- at rem Determine the command interpreter to execute the "CD" later
-set COMMAND_COM="cmd.exe"
-if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
-if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
-
- at rem Use explicit find.exe to prevent cygwin and others find.exe from being used
-set FIND_EXE="find.exe"
-if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
-if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
-
-:check_JAVA_HOME
- at rem Make sure we have a valid JAVA_HOME
-if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
-
-echo.
-echo ERROR: Environment variable JAVA_HOME has not been set.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-echo.
-goto end
-
-:have_JAVA_HOME
- at rem Validate JAVA_HOME
-%COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
-if not errorlevel 1 goto init
-
-echo.
-echo ERROR: JAVA_HOME might be set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation if there are problems.
-echo.
-
-:init
- at rem get name of script to launch with full path
- at rem Get command-line arguments, handling Windowz variants
-SET _marker=%JAVA_HOME: =%
- at rem IF NOT "%_marker%" == "%JAVA_HOME%" ECHO JAVA_HOME "%JAVA_HOME%" contains spaces. Please change to a location without spaces if this causes problems.
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%eval[2+2]" == "4" goto 4NT_args
-
-IF "%_marker%" == "%JAVA_HOME%" goto :win9xME_args
-
-set _FIXPATH=
-call :fixpath "%JAVA_HOME%"
-set JAVA_HOME=%_FIXPATH:~1%
-
-goto win9xME_args
-
-:fixpath
-if not %1.==. (
-for /f "tokens=1* delims=;" %%a in (%1) do (
-call :shortfilename "%%a" & call :fixpath "%%b"
-)
-)
-goto :EOF
-:shortfilename
-for %%i in (%1) do set _FIXPATH=%_FIXPATH%;%%~fsi
-goto :EOF
-
-
-:win9xME_args
- at rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
- at rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
- at rem Setup the command line
diff --git a/subprojects/gradle-wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java b/subprojects/gradle-wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
index f9f0070..b167da3 100644
--- a/subprojects/gradle-wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
+++ b/subprojects/gradle-wrapper/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.java
@@ -18,6 +18,7 @@ package org.gradle.api.tasks.wrapper;
 
 import org.gradle.api.internal.AbstractTask;
 import org.gradle.api.tasks.AbstractTaskTest;
+import org.gradle.api.tasks.wrapper.internal.WrapperScriptGenerator;
 import org.gradle.util.GUtil;
 import org.gradle.util.TemporaryFolder;
 import org.gradle.util.TestFile;
diff --git a/wrapper/gradle-wrapper.properties b/wrapper/gradle-wrapper.properties
index 6ed33e0..6c0b354 100644
--- a/wrapper/gradle-wrapper.properties
+++ b/wrapper/gradle-wrapper.properties
@@ -18,8 +18,8 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
-distributionVersion=0.9-20100727090849+0200
+distributionVersion=0.9-rc-1
 zipStorePath=wrapper/dists
-urlRoot=http\://gradle.artifactoryonline.com/gradle/distributions/gradle-snapshots
+urlRoot=http\://dist.codehaus.org/gradle
 distributionName=gradle
 distributionClassifier=bin

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/gradle-1.12.git



More information about the pkg-java-commits mailing list